From 788708fb744799974758580dbb1f67275d3f2f2d Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Sat, 11 Jun 2022 03:16:15 +0200 Subject: [PATCH] Started work on CAN bus --- software/main/CMakeLists.txt | 2 +- software/main/include/can.h | 5 + software/main/include/can_data.h | 58 +++++ software/main/src/can.cpp | 428 +++++++++++++++++++++++++++++++ software/main/src/main.cpp | 87 +++++++ 5 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 software/main/include/can.h create mode 100644 software/main/include/can_data.h create mode 100644 software/main/src/can.cpp diff --git a/software/main/CMakeLists.txt b/software/main/CMakeLists.txt index 7906b21..5eef017 100644 --- a/software/main/CMakeLists.txt +++ b/software/main/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "src/main.cpp" "src/helper.cpp" "src/wav.cpp" "src/storage.cpp" "src/i2s.cpp" "src/bluetooth.cpp" "src/avrcp.cpp" "src/a2dp.cpp" +idf_component_register(SRCS "src/main.cpp" "src/helper.cpp" "src/wav.cpp" "src/storage.cpp" "src/i2s.cpp" "src/bluetooth.cpp" "src/avrcp.cpp" "src/a2dp.cpp" "src/can.cpp" INCLUDE_DIRS "include" EMBED_FILES "assets/connect.wav" "assets/disconnect.wav") diff --git a/software/main/include/can.h b/software/main/include/can.h new file mode 100644 index 0000000..34278b5 --- /dev/null +++ b/software/main/include/can.h @@ -0,0 +1,5 @@ +#pragma once + +namespace can { + void init(); +} diff --git a/software/main/include/can_data.h b/software/main/include/can_data.h new file mode 100644 index 0000000..fd78009 --- /dev/null +++ b/software/main/include/can_data.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +namespace can { + #pragma pack(1) + struct Buttons { + uint8_t _1 : 1; + bool source : 1; + bool volume_down : 1; + bool volume_up : 1; + uint8_t _2 : 2; + bool backward : 1; + bool forward : 1; + + uint8_t scroll : 8; + + uint8_t _3 : 8; + }; + #pragma pack() + + enum Source : uint8_t { + Bluetooth = 0b111, + USB = 0b110, + AUX2 = 0b101, + AUX1 = 0b100, + CD_Changer = 0b011, + CD = 0b010, + Tuner = 0b001 + }; + + enum DiskStatus : uint8_t { + Init = 0b00, + Unavailable = 0b01, + Available = 0b10 + }; + + #pragma pack(1) + struct Radio { + uint8_t _1 : 5; + bool muted : 1; + uint8_t _2 : 1; + bool enabled : 1; + + uint8_t _3 : 4; + bool cd_changer_available : 1; + DiskStatus disk_status : 2; + uint8_t _4 : 1; + + uint8_t _5 : 4; + Source source : 3; + uint8_t _6 : 1; + + uint8_t _7 : 8; + }; + #pragma pack() +} diff --git a/software/main/src/can.cpp b/software/main/src/can.cpp new file mode 100644 index 0000000..f4ef656 --- /dev/null +++ b/software/main/src/can.cpp @@ -0,0 +1,428 @@ +#include +#include + +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/portmacro.h" +#include "freertos/task.h" +#include "driver/spi_common.h" +#include "driver/spi_master.h" +#include "esp_err.h" +#include "esp_log.h" +#include "hal/spi_types.h" + +#include "can.h" + +#define CAN_TAG "APP_CAN" + +#define PIN_NUM_MOSI 23 +#define PIN_NUM_MISO 19 +#define PIN_NUM_CLK 18 +#define PIN_NUM_CS 5 + +#define MCP_SIDH 0 +#define MCP_SIDL 1 +#define MCP_EID8 2 +#define MCP_EID0 3 + +#define MCP_TXB_EXIDE_M 0x08 +#define MCP_DLC_MASK 0x0F +#define MCP_RTR_MASK 0x40 + +#define MCP_RXB_RX_STDEXT 0x00 +#define MCP_RXB_RX_MASK 0x60 +#define MCP_RXB_BUKT_MASK (1<<2) + +#define MCP_STAT_TXIF_MASK 0xA8 +#define MCP_STAT_TX0IF 0x08 +#define MCP_STAT_TX1IF 0x20 +#define MCP_STAT_TX2IF 0x80 +#define MCP_STAT_RXIF_MASK 0x03 + +// Instructions +#define MCP_WRITE 0x02 +#define MCP_READ 0x03 +#define MCP_BITMOD 0x05 +#define MCP_READ_RX0 0x90 +#define MCP_READ_RX1 0x94 +#define MCP_READ_STATUS 0xA0 +#define MCP_RESET 0xC0 + +// Registers +#define MCP_CANSTAT 0x0E +#define MCP_CANCTRL 0x0F +#define MCP_CNF3 0x28 +#define MCP_CNF2 0x29 +#define MCP_CNF1 0x2A +#define MCP_CANINTE 0x2B +#define MCP_CANINTF 0x2C +#define MCP_TXB0CTRL 0x30 +#define MCP_TXB1CTRL 0x40 +#define MCP_TXB2CTRL 0x50 +#define MCP_RXB0CTRL 0x60 +#define MCP_RXB1CTRL 0x70 + +// CANCTRL Register values +#define MODE_NORMAL 0x00 +#define MODE_SLEEP 0x20 +#define MODE_LISTENONLY 0x60 +#define MODE_CONFIG 0x80 +#define MODE_MASK 0xE0 + +// CANINTF Register bits +#define MCP_RX0IF 0x01 +#define MCP_RX1IF 0x02 +#define MCP_TX0IF 0x04 +#define MCP_TX1IF 0x08 +#define MCP_TX2IF 0x10 +#define MCP_WAKIF 0x40 + +static void send_cmd(spi_device_handle_t spi, const uint8_t cmd) { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + t.tx_buffer = &cmd; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + +} + +static uint8_t read_register(spi_device_handle_t spi, const uint8_t address) { + // Acquire the bus in order to use SPI_TRANS_CS_KEEP_ACTIVE + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + ESP_ERROR_CHECK(ret); + + // Send the command + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8*2; + uint8_t cmd[] = { MCP_READ, address }; + t.tx_buffer = cmd; + t.flags = SPI_TRANS_CS_KEEP_ACTIVE; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + } + + // Read the data + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + t.flags = SPI_TRANS_USE_RXDATA; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + spi_device_release_bus(spi); + ESP_ERROR_CHECK(ret); + + return t.rx_data[0]; +} + +static void set_register(spi_device_handle_t spi, const uint8_t address, const uint8_t value) { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8*3; + uint8_t cmd[] = { MCP_WRITE, address, value }; + t.tx_buffer = cmd; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); +} + +static void modify_register(spi_device_handle_t spi, const uint8_t address, const uint8_t mask, const uint8_t data) { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8*4; + uint8_t cmd[] = { MCP_BITMOD, address, mask, data }; + t.tx_buffer = cmd; + + esp_err_t ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); +} + +static bool request_new_mode(spi_device_handle_t spi, const uint8_t new_mode) { + unsigned long start = esp_timer_get_time() / 1000; + + for (;;) { + modify_register(spi, MCP_CANCTRL, MODE_MASK, new_mode); + uint8_t state = read_register(spi, MCP_CANSTAT); + if ((state & MODE_MASK) == new_mode) { + return true; + } else if ((esp_timer_get_time() / 1000 - start) > 200) { + ESP_LOGE(CAN_TAG, "Failed to set new mode"); + abort(); + } + } + +} + +static uint8_t get_mode(spi_device_handle_t spi) { + return read_register(spi, MCP_CANSTAT) & MODE_MASK; +} + +static void set_CANCTRL_mode(spi_device_handle_t spi, uint8_t new_mode) { + ESP_LOGI(CAN_TAG, "set_CANCTRL_mode"); + if (get_mode(spi) == MODE_SLEEP && new_mode != MODE_SLEEP) { + ESP_LOGI(CAN_TAG, "if"); + uint8_t wake_int_enabled = (read_register(spi, MCP_CANINTE) & MCP_WAKIF); + if (!wake_int_enabled) { + modify_register(spi, MCP_CANINTE, MCP_WAKIF, MCP_WAKIF); + } + + modify_register(spi, MCP_CANINTF, MCP_WAKIF, MCP_WAKIF); + + request_new_mode(spi, MODE_LISTENONLY); + + if (!wake_int_enabled) { + modify_register(spi, MCP_CANINTE, MCP_WAKIF, 0); + } + } + + modify_register(spi, MCP_CANINTF, MCP_WAKIF, 0); + + request_new_mode(spi, new_mode); + ESP_LOGI(CAN_TAG, "done!"); +} + +static void config_rate(spi_device_handle_t spi) { + // This is for 16MHz, 125kBPS + set_register(spi, MCP_CNF1, 0x03); + set_register(spi, MCP_CNF2, 0xF0); + set_register(spi, MCP_CNF3, 0x86); +} + +static void init_CAN_buffers(spi_device_handle_t spi) { + uint8_t a1 = MCP_TXB0CTRL; + uint8_t a2 = MCP_TXB1CTRL; + uint8_t a3 = MCP_TXB2CTRL; + + for (int i = 0; i < 14; i++) { + set_register(spi, a1, 0); + set_register(spi, a2, 0); + set_register(spi, a3, 0); + + a1++; + a2++; + a3++; + } + + set_register(spi, MCP_RXB0CTRL, 0); + set_register(spi, MCP_RXB1CTRL, 0); +} + +static uint8_t read_status(spi_device_handle_t spi) { + // Acquire the bus in order to use SPI_TRANS_CS_KEEP_ACTIVE + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + ESP_ERROR_CHECK(ret); + + // Send the command + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + uint8_t cmd[] = { MCP_READ_STATUS }; + t.tx_buffer = cmd; + t.flags = SPI_TRANS_CS_KEEP_ACTIVE; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + } + + // Read the data + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + t.flags = SPI_TRANS_USE_RXDATA; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + spi_device_release_bus(spi); + ESP_ERROR_CHECK(ret); + + return t.rx_data[0]; +} + +static bool available(spi_device_handle_t spi) { + uint8_t res = read_status(spi); + + return res & MCP_STAT_RXIF_MASK; +} + +static uint8_t read_rx_tx_status(spi_device_handle_t spi) { + uint8_t ret = (read_status(spi) & (MCP_STAT_TXIF_MASK | MCP_STAT_RXIF_MASK)); + + ret = (ret & MCP_STAT_TX0IF ? MCP_TX0IF : 0) | + (ret & MCP_STAT_TX1IF ? MCP_TX1IF : 0) | + (ret & MCP_STAT_TX2IF ? MCP_TX2IF : 0) | + (ret & MCP_STAT_RXIF_MASK); + + return ret; +} + +static void read_can_msg(spi_device_handle_t spi, uint8_t buffer_load_addr, uint8_t* id, uint8_t* ext, uint8_t* rtr_bit, uint8_t* len, uint8_t* buf) { + // Acquire the bus in order to use SPI_TRANS_CS_KEEP_ACTIVE + esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY); + ESP_ERROR_CHECK(ret); + + // Send the command + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + uint8_t cmd[] = { buffer_load_addr }; + t.tx_buffer = cmd; + t.flags = SPI_TRANS_CS_KEEP_ACTIVE; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + } + + // Read id + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8*4; + t.flags = SPI_TRANS_USE_RXDATA; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + *id = (t.rx_data[MCP_SIDH] << 3) + (t.rx_data[MCP_SIDL] >> 5); + *ext = 0; + if ((t.rx_data[MCP_SIDL] & MCP_TXB_EXIDE_M) == MCP_TXB_EXIDE_M) { + // Extended id + // @TODO Do we need this for our application + *id = (*id << 2) + (t.rx_data[MCP_SIDL] & 0x03); + *id = (*id << 8) + t.rx_data[MCP_EID8]; + *id = (*id << 8) + t.rx_data[MCP_EID0]; + *ext = 1; + } + } + + // Read the message size + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8; + t.flags = SPI_TRANS_USE_RXDATA; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + *len = t.rx_data[0] & MCP_DLC_MASK; + + // @TODO Do we need this in our application + *rtr_bit = (t.rx_data[0] & MCP_RTR_MASK) ? 1 : 0; + } + + // Read the data + { + spi_transaction_t t; + memset(&t, 0, sizeof(t)); + t.length = 8*(*len); + t.flags = SPI_TRANS_USE_RXDATA; + + ret = spi_device_polling_transmit(spi, &t); + ESP_ERROR_CHECK(ret); + + // Copy the data to the output buffer + for (int i = 0; i < *len; i++) { + buf[i] = t.rx_data[i]; + } + } + + // Make sure we release the bus + spi_device_release_bus(spi); + ESP_ERROR_CHECK(ret); +} + +static void read_message(spi_device_handle_t spi) { + uint8_t status = read_rx_tx_status(spi); + + uint8_t id; + uint8_t ext; + uint8_t rtr_bit; + uint8_t len; + uint8_t buf[8]; + + if (status & MCP_RX0IF) { + read_can_msg(spi, MCP_READ_RX0, &id, &ext, &rtr_bit, &len, buf); + } else if (status & MCP_RX1IF) { + read_can_msg(spi, MCP_READ_RX0, &id, &ext, &rtr_bit, &len, buf); + } + + ESP_LOGI(CAN_TAG, "Received: id=%i, ext=%i, rtr_bit=%i, len=%i", id, ext, rtr_bit, len); + // @TODO Print the buf data +} + +void can_task(void* params) { + spi_device_handle_t spi = *(spi_device_handle_t*)params; + + for (;;) { + if (available(spi)) { + ESP_LOGI(CAN_TAG, "Available!"); + read_message(spi); + } else { + ESP_LOGI(CAN_TAG, "Nothing..."); + } + } +} + +void can::init() { + ESP_LOGI(CAN_TAG, "Initializing can"); + + spi_bus_config_t buscfg = { + .mosi_io_num=PIN_NUM_MOSI, + .miso_io_num=PIN_NUM_MISO, + .sclk_io_num=PIN_NUM_CLK, + .quadwp_io_num=-1, + .quadhd_io_num=-1, + .max_transfer_sz=0 + }; + esp_err_t ret = spi_bus_initialize(VSPI_HOST, &buscfg, SPI_DMA_CH_AUTO); + ESP_ERROR_CHECK(ret); + + ESP_LOGI(CAN_TAG, "Adding device"); + spi_device_interface_config_t devcfg = { + .mode=0, + .clock_speed_hz=10*1000*1000, + .spics_io_num=PIN_NUM_CS, + .queue_size=7, + }; + spi_device_handle_t spi; + ret = spi_bus_add_device(VSPI_HOST, &devcfg, &spi); + ESP_ERROR_CHECK(ret); + + ESP_LOGI(CAN_TAG, "Resetting MCP1525"); + send_cmd(spi, MCP_RESET); + // @TODO Check if this is really needed + // Copied from other libary, here to give the MCP2515 time to wake up + vTaskDelay(5 / portTICK_PERIOD_MS); + + ESP_LOGI(CAN_TAG, "Enter config mode"); + set_CANCTRL_mode(spi, MODE_CONFIG); + + ESP_LOGI(CAN_TAG, "Setting rate"); + config_rate(spi); + + ESP_LOGI(CAN_TAG, "Init CAN buffers"); + init_CAN_buffers(spi); + + ESP_LOGI(CAN_TAG, "Setting interrupt mode"); + set_register(spi, MCP_CANINTE, MCP_RX0IF | MCP_RX1IF); + + ESP_LOGI(CAN_TAG, "Enable receive buffers"); + modify_register(spi, MCP_RXB0CTRL, MCP_RXB_RX_MASK | MCP_RXB_BUKT_MASK, MCP_RXB_RX_STDEXT | MCP_RXB_BUKT_MASK); + modify_register(spi, MCP_RXB1CTRL, MCP_RXB_RX_MASK, MCP_RXB_RX_STDEXT); + + ESP_LOGI(CAN_TAG, "Enter normal mode"); + set_CANCTRL_mode(spi, MODE_NORMAL); + + ESP_LOGI(CAN_TAG, "Init done!"); + + xTaskCreate(can_task, "CAN Task", 4096, &spi, 0, nullptr); +} diff --git a/software/main/src/main.cpp b/software/main/src/main.cpp index 917d0b9..4bd0adb 100644 --- a/software/main/src/main.cpp +++ b/software/main/src/main.cpp @@ -7,12 +7,89 @@ #include "bluetooth.h" #include "avrcp.h" #include "a2dp.h" +#include "can.h" + +#include "can_data.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #define APP_TAG "APP" +static void print_buttons(can::Buttons buttons) { + ESP_LOGI(APP_TAG, "BUTTONS"); + if (buttons.forward) { + ESP_LOGI(APP_TAG, "Button: F"); + } + if (buttons.backward) { + ESP_LOGI(APP_TAG, "Button: B"); + } + if (buttons.volume_up) { + ESP_LOGI(APP_TAG, "Button: U"); + } + if (buttons.volume_down) { + ESP_LOGI(APP_TAG, "Button: D"); + } + if (buttons.source) { + ESP_LOGI(APP_TAG, "Button: S"); + } + ESP_LOGI(APP_TAG, "Scroll: %i", buttons.scroll); +} + +static void print_radio(can::Radio radio) { + ESP_LOGI(APP_TAG, "RADIO"); + if (radio.enabled) { + ESP_LOGI(APP_TAG, "Enabled: %i", radio.enabled); + } + if (radio.muted) { + ESP_LOGI(APP_TAG, "Muted: %i", radio.muted); + } + if (radio.cd_changer_available) { + ESP_LOGI(APP_TAG, "CD Changer: %i", radio.cd_changer_available); + } + switch (radio.disk_status) { + case can::DiskStatus::Init: + ESP_LOGI(APP_TAG, "CD: Init"); + break; + case can::DiskStatus::Unavailable: + ESP_LOGI(APP_TAG, "CD: Unavailable"); + break; + case can::DiskStatus::Available: + ESP_LOGI(APP_TAG, "CD: Available"); + break; + default: + ESP_LOGW(APP_TAG, "CD: Invalid"); + break; + } + + switch (radio.source) { + case can::Source::Bluetooth: + ESP_LOGI(APP_TAG, "Source: Bluetooth"); + break; + case can::Source::USB: + ESP_LOGI(APP_TAG, "Source: USB"); + break; + case can::Source::AUX2: + ESP_LOGI(APP_TAG, "Source: AUX2"); + break; + case can::Source::AUX1: + ESP_LOGI(APP_TAG, "Source: AUX1"); + break; + case can::Source::CD_Changer: + ESP_LOGI(APP_TAG, "Source: CD Changer"); + break; + case can::Source::CD: + ESP_LOGI(APP_TAG, "Source: CD"); + break; + case can::Source::Tuner: + ESP_LOGI(APP_TAG, "Source: Tuner"); + break; + default: + ESP_LOGW(APP_TAG, "Source: Invalid"); + break; + } +} + extern "C" void app_main() { ESP_LOGI(APP_TAG, "Starting Car Stereo"); ESP_LOGI(APP_TAG, "Available Heap: %u", esp_get_free_heap_size()); @@ -25,5 +102,15 @@ extern "C" void app_main() { a2dp::init(); a2dp::connect_to_last(); + + can::init(); + + /* uint8_t button_buffer[3] = { 0b01000110, 22, 0x00 }; */ + /* can::Buttons buttons = *(can::Buttons*)button_buffer; */ + /* print_buttons(buttons); */ + + /* uint8_t radio_buffer[4] = { 0b10100000, 0b01000000, 0b00110000, 0x00 }; */ + /* can::Radio radio = *(can::Radio*)radio_buffer; */ + /* print_radio(radio); */ }