main: add LCD frame decoding

This commit is contained in:
King Kévin 2023-03-13 19:40:28 +01:00
parent 66816abc9f
commit 0756bb8fe5
1 changed files with 184 additions and 1 deletions

View File

@ -6,8 +6,11 @@
#include <sys/reent.h>
#include "esp_log.h"
#include "esp_mac.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "tusb_dfu_rt.h"
@ -41,6 +44,134 @@
static const char *TAG = "main";
/* the MCU sends 1 or 17 bytes frame the to LCD controller
* the below described which bit corresponds to which segment
* digit 1 is left most on the LCD, bit 7 is the most significant bit.
*
* byte 0:
* - 0x80: initialize LCD controller (1 byte frame)
* - 0xa0: set segments (17 bytes frame)
*
* byte 1:
* - bit 7: no segment
* - bit 6: no segment
* - bit 5: battery low level
* - bit 4: battery mid level
* - bit 3: battery high level
* - bit 2: OVER
* - bit 1: USB
* - bit 0: Bluetooth icon
*
* byte 2:
* - bit 7: battery case
* - bit 6: MAX
* - bit 5: MIN
* - bit 4: HOLD
* - bit 3: digit 1, segment b and segment c
* - bit 2: digit 2, segment f
* - bit 1: digit 2, segment g
* - bit 0: digit 2, segment e
*
* byte 3:
* - bit 7: FAST
* - bit 6: digit 2, segment a
* - bit 5: digit 2, segment b
* - bit 4: digit 2, segment c
* - bit 3: digit 2, segment d
* - bit 2: digit 3, segment f
* - bit 1: digit 3, segment g
* - bot 0: digit 3, segment e
*
* byte 4:
* - bit 7: SLOW
* - bit 6: digit 3, segment a
* - bit 5: digit 3, segment b
* - bit 4: digit 3, segment c
* - bit 3: digit 3, segment d
* - bit 2: no segment
* - bit 1: no segment
* - bit 0: digit 3, segment dp
*
* byte 5:
* - bit 7: dBC
* - bit 6: digit 4, segment f
* - bit 5: digit 4, segment g
* - bit 4: digit 4, segment e
* - bit 3: dBA
* - bit 2: digit 4, segment a
* - bit 1: digit 4, segment b
* - bit 0: digit 4, segment c
*
* byte 6:
* - bit 7: digit 4, segment d
*
* all other bits can be set to any value
*/
// number of bytes in an LCD frame
#define FRAME_SIZE (17U)
/** which bit should be set to enable which segment in specific digits
* @note 0 = byte 0 bit 0
*/
static const uint8_t digit_segments[4][8] = {
{ // digit 1
0, // a (segment does not exist)
16 + 3, // b
16 + 3, // c
0, // d (segment does not exist)
0, // e (segment does not exist)
0, // f (segment does not exist)
0, // g (segment does not exist)
0, // dp (segment does not exist)
},
{ // digit 2
24 + 6, // a
24 + 5, // b
24 + 4, // c
24 + 3, // d
16 + 0, // e
16 + 2, // f
16 + 1, // g
0, // dp (segment does not exist)
},
{ // digit 3
32 + 6, // a
32 + 5, // b
32 + 4, // c
32 + 3, // d
24 + 0, // e
24 + 2, // f
24 + 1, // g
0, // dp (actually 24 + 0 but we don't use it)
},
{ // digit 4
40 + 2, // a
40 + 1, // b
40 + 0, // c
48 + 7, // d
40 + 4, // e
40 + 6, // f
40 + 5, // g
0, // dp (segment does not exist)
},
};
/** the index corresponds to the digit to be displayed, the value represent the segments to be enabled
* @note LSb = a, MSb = dp
*/
static const uint8_t number_segments[10] = {
0x3f, // 0: a b c d e f
0x06, // 1: b c
0x5b, // 2: a b d e g
0x4f, // 3: a b c d g
0x66, // 4: b c f g
0x6d, // 5: a c d f g
0x7d, // 6: a c d e f g
0x07, // 7: a b c
0x7f, // 8: a b c d e f g
0x6f, // 9; a c b d f g
};
/**
* @brief USB string descriptor
*/
@ -49,11 +180,58 @@ static const char* usb_str_desc[USB_STRING_DESCRIPTOR_ARRAY_SIZE] = {
CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, // 1: Manufacturer
CONFIG_TINYUSB_DESC_PRODUCT_STRING, // 2: Product
CONFIG_TINYUSB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID
CONFIG_TINYUSB_DESC_CDC_STRING, // 4: CDC Interface
"GM1532 measurements", // 4: CDC Interface
"", // 5: MSC Interface
"DFU (runtime mode)" // 6: DFU RT
};
static QueueHandle_t meas_queue; // queue for the measurement messages received
#define MEAS_QUEUE_SIZE 4 // max number of control message received
/* handle measurement received */
static void meas_task(void *pvParameter)
{
spi_slave_transaction_t t;
while (xQueueReceive(meas_queue, &t, portMAX_DELAY) == pdTRUE) {
const uint8_t* rx_buffer = t.rx_buffer; // t.rx_buffer is type-less
if (t.trans_len > 6 && 0xa0 == rx_buffer[0]) { // frame long enough for the LCD data
// extract digit segments
uint8_t segments[4] = {0}; // which digit segments are enabled
for (uint8_t digit = 0; digit < LENGTH(segments); digit++) {
segments[digit] = 0; // clear all segments
for (uint8_t seg = 0; seg < 8; seg++) {
if (rx_buffer[digit_segments[digit][seg] / 8] & (1 << (digit_segments[digit][seg] % 8))) {
segments[digit] |= (1 << seg);
}
}
}
// figure out digit value
uint8_t digits[4] = {0};
for (uint8_t digit = 0; digit < LENGTH(digits); digit++) {
for (uint8_t i = 0; i < LENGTH(number_segments); i++) {
if (number_segments[i] == segments[digit]) {
digits[digit] = i;
}
}
}
const uint16_t meas = digits[0] * 1000 + digits[1] * 100 + digits[2] * 10 + digits[3]; // in deci dBA
if (meas < 1400) { // ignore all segments on out of range values
printf("%u.%u dBA\n", meas / 10, meas % 10);
}
}
}
}
// called after LCD frame (e.g. SPI transaction) is received
static uint8_t timeout = 0;
void post_trans_cb(spi_slave_transaction_t *t) {
if (t) {
ESP_ERROR_CHECK( xQueueSend(meas_queue, t, 512) != pdTRUE ); // send measurement to be displayed
spi_slave_queue_trans(SPI2_HOST, t, portMAX_DELAY); // re-add transaction
}
timeout = 0; // reset timeout since we received data
}
void app_main(void)
{
// check DFU force
@ -105,6 +283,11 @@ void app_main(void)
ESP_ERROR_CHECK( tusb_dfu_rf_init() ); // configure DFU runtime (ensures we can use it)
ESP_LOGI(TAG, "USB initialized");
// task to decode and show the measurements
meas_queue = xQueueCreate(MEAS_QUEUE_SIZE, sizeof(spi_slave_transaction_t));
ESP_ERROR_CHECK( NULL == meas_queue );
xTaskCreate(meas_task, "meas_task", 2048, NULL, 4, NULL);
// configure SPI for GM1352 LCD data frame reading
const spi_bus_config_t buscfg = { // SPI bus configuration
.mosi_io_num = GPIO_MOSI,