diff --git a/main/main.c b/main/main.c index 9846e35..301af6c 100644 --- a/main/main.c +++ b/main/main.c @@ -6,8 +6,11 @@ #include #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,