From 7a8e9591e324637d80e4bfd18f1b75774c521dda Mon Sep 17 00:00:00 2001 From: bondus Date: Thu, 25 Jun 2020 00:59:38 +0200 Subject: [PATCH] stm32: Improved CAN support for STM32 (#2976) Reworked the STM32F0 CAN bus implementation. It's more robust and higher performance. Added support for function remapping to different pins. API is emulating an STM32F0. Improved and ported CAN bus to STM32F0, F1 and F4. Signed-off-by: Pontus Borg --- src/stm32/Kconfig | 20 +- src/stm32/Makefile | 2 +- src/stm32/can.c | 440 +++++++++++++++++++++++++++++ src/stm32/{stm32f0_can.h => can.h} | 10 +- src/stm32/stm32f0.c | 2 +- src/stm32/stm32f0_can.c | 267 ----------------- src/stm32/stm32f1.c | 31 +- 7 files changed, 493 insertions(+), 279 deletions(-) create mode 100644 src/stm32/can.c rename src/stm32/{stm32f0_can.h => can.h} (52%) delete mode 100644 src/stm32/stm32f0_can.c diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index 5d6ef82b..bd0388da 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -159,18 +159,36 @@ config USBSERIAL bool "Use USB for communication (instead of serial)" depends on HAVE_STM32_USBFS || HAVE_STM32_USBOTG default y + config CANSERIAL bool "Use CAN for communication (instead of serial)" - depends on MACH_STM32F042 && !USBSERIAL + depends on !USBSERIAL default n config SERIAL_BAUD int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANSERIAL default 500000 if CANSERIAL +choice + depends on CANSERIAL + prompt "CAN pins" + config CAN_PINS_PA11_PA12 + bool "Pins PA11(rx) and PA12(tx)" + config CAN_PINS_PB8_PB9 + bool "Pins PB8(rx) and PB9(tx)" + config CAN_PINS_PI8_PH13 + bool "Pins PI8(rx) and PH13(tx)" if MACH_STM32F4 + config CAN_PINS_PB5_PB6 + bool "Pins PB5(rx) and PB6(tx)" if MACH_STM32F4 + config CAN_PINS_PB12_PB13 + bool "Pins PB12(rx) and PB13(tx)" if MACH_STM32F4 +endchoice + config STM32F042_PIN_SWAP bool "Use PA9/PA10 for USB or CAN" if MACH_STM32F042 depends on (USBSERIAL || CANSERIAL) && MACH_STM32F042 default y if (USBSERIAL || CANSERIAL) default n + help + Remaps logical pins PA11/PA12 to physical PA9/PA10 on low pincount F042 devices. config SERIAL depends on !USBSERIAL && !CANSERIAL bool diff --git a/src/stm32/Makefile b/src/stm32/Makefile index 98b52e4c..56f1581c 100644 --- a/src/stm32/Makefile +++ b/src/stm32/Makefile @@ -46,7 +46,7 @@ src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c serial-src-y := stm32/serial.c serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c src-$(CONFIG_SERIAL) += $(serial-src-y) generic/serial_irq.c -can-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_can.c +can-src-$(CONFIG_CANSERIAL) += stm32/can.c src-$(CONFIG_CANSERIAL) += $(can-src-y) generic/serial_irq.c # Binary output file rules diff --git a/src/stm32/can.c b/src/stm32/can.c new file mode 100644 index 00000000..7e3d75f1 --- /dev/null +++ b/src/stm32/can.c @@ -0,0 +1,440 @@ +/* + * Serial over CAN emulation for STM32 boards. + * + * Copyright (C) 2019 Eug Krashtan + * Copyright (C) 2020 Pontus Borg + * This file may be distributed under the terms of the GNU GPLv3 license. + * + */ + +#include "autoconf.h" // +#include "board/armcm_boot.h" // armcm_enable_irq +#include "board/serial_irq.h" // serial_rx_byte +#include "command.h" // DECL_CONSTANT_STR +#include "internal.h" // enable_pclock +#include "sched.h" // DECL_INIT +#include +#include "can.h" + +#if (CONFIG_CAN_PINS_PA11_PA12) +DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PA11,PA12"); +#define GPIO_Rx GPIO('A', 11) +#define GPIO_Tx GPIO('A', 12) +#endif +#if (CONFIG_CAN_PINS_PB8_PB9) +DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB8,PB9"); +#define GPIO_Rx GPIO('B', 8) +#define GPIO_Tx GPIO('B', 9) +#endif +#if (CONFIG_CAN_PINS_PI8_PH13) +DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PI8,PH13"); +#define GPIO_Rx GPIO('I', 8) +#define GPIO_Tx GPIO('H', 13) +#endif +#if (CONFIG_CAN_PINS_PB5_PB6) +DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB5,PB6"); +#define GPIO_Rx GPIO('B', 5) +#define GPIO_Tx GPIO('B', 6) +#endif +#if (CONFIG_CAN_PINS_PB12_PB13) +DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB12,PB13"); +#define GPIO_Rx GPIO('B', 12) +#define GPIO_Tx GPIO('B', 13) +#endif + +#if (CONFIG_MACH_STM32F0) +#define SOC_CAN CAN +#define CAN_RX0_IRQn CEC_CAN_IRQn +#define CAN_RX1_IRQn CEC_CAN_IRQn +#define CAN_TX_IRQn CEC_CAN_IRQn +#define CAN_SCE_IRQn CEC_CAN_IRQn +#define CAN_FUNCTION GPIO_FUNCTION(4) // Alternative function mapping number +#endif + +#if (CONFIG_MACH_STM32F1) +#define SOC_CAN CAN1 +#define CAN_RX0_IRQn CAN1_RX0_IRQn +#define CAN_RX1_IRQn CAN1_RX1_IRQn +#define CAN_TX_IRQn CAN1_TX_IRQn +#define CAN_SCE_IRQn CAN1_SCE_IRQn +#define CAN_FUNCTION GPIO_FUNCTION(9) // Alternative function mapping number +#endif + + +#if (CONFIG_MACH_STM32F4) +#warning CAN on STM32F4 is untested +#if (CONFIG_CAN_PINS_PA11_PA12 || + CONFIG_CAN_PINS_PB8_PB9 || + CONFIG_CAN_PINS_PI8_PH13) +#define SOC_CAN CAN1 +#define CAN_RX0_IRQn CAN1_RX0_IRQn +#define CAN_RX1_IRQn CAN1_RX1_IRQn +#define CAN_TX_IRQn CAN1_TX_IRQn +#define CAN_SCE_IRQn CAN1_SCE_IRQn +#elsif ((CONFIG_CAN_PINS_PB5_PB6 || CONFIG_CAN_PINS_PB12_PB13) +#define SOC_CAN CAN2 +#define CAN_RX0_IRQn CAN2_RX0_IRQn +#define CAN_RX1_IRQn CAN2_RX1_IRQn +#define CAN_TX_IRQn CAN2_TX_IRQn +#define CAN_SCE_IRQn CAN2_SCE_IRQn +#else +#error Uknown pins for STMF32F4 CAN +#endif + +#define CAN_FUNCTION GPIO_FUNCTION(9) // Alternative function mapping number +#endif + + +#ifndef SOC_CAN +#error No known CAN device for configured MCU +#endif + + +// TXFP makes packets posted to the TX mboxes transmit in chronologcal order +// ABOM makes the hardware automatically leave bus-off state +#define MCR_FLAGS (CAN_MCR_TXFP | CAN_MCR_ABOM) + +#define CAN_FILTER_NUMBER 0 + +static uint16_t MyCanId = 0; + +static int can_find_empty_tx_mbox(void) { + uint32_t tsr = SOC_CAN->TSR; + if(tsr & CAN_TSR_TME0) return 0; + if(tsr & CAN_TSR_TME1) return 1; + if(tsr & CAN_TSR_TME2) return 2; + return -1; +} + +static void can_transmit_mbox(uint32_t id, int mbox, uint32_t dlc, uint8_t *pkt) +{ + CAN_TxMailBox_TypeDef *mb = &SOC_CAN->sTxMailBox[mbox]; + /* Set up the Id */ + mb->TIR &= CAN_TI0R_TXRQ; + mb->TIR |= (id << CAN_TI0R_STID_Pos); + + /* Set up the DLC */ + mb->TDTR &= 0xFFFFFFF0U; + mb->TDTR |= (dlc & 0xFU); + + /* Set up the data field */ + if(pkt) { + mb->TDLR = ((uint32_t)pkt[3] << 24) | + ((uint32_t)pkt[2] << 16) | + ((uint32_t)pkt[1] << 8) | + ((uint32_t)pkt[0] << 0); + mb->TDHR = ((uint32_t)pkt[7] << 24) | + ((uint32_t)pkt[6] << 16) | + ((uint32_t)pkt[5] << 8) | + ((uint32_t)pkt[4] << 0); + } + + /* Request transmission */ + __sync_synchronize(); // disable write optimization + mb->TIR |= CAN_TI0R_TXRQ; + +} + +// Blocking transmit function, it can race with the IRQ driven TX handler. +// This should(tm) not happen +static void can_transmit(uint32_t id, uint32_t dlc, uint8_t *pkt) +{ + int mbox = -1; + + do { + mbox = can_find_empty_tx_mbox(); + } while(mbox < 0); + + can_transmit_mbox(id, mbox, dlc, pkt); +} + +// Convert Unique 96-bit value into 48 bit representation +static void pack_uuid(uint8_t* u) +{ + for(int i=0; isFIFOMailBox[mbox].RDLR; + buf[0] = (rdlr >> 0) & 0xff; + buf[1] = (rdlr >> 8) & 0xff; + buf[2] = (rdlr >> 16) & 0xff; + buf[3] = (rdlr >> 24) & 0xff; + uint32_t rdhr = SOC_CAN->sFIFOMailBox[mbox].RDHR; + buf[4] = (rdhr >> 0) & 0xff; + buf[5] = (rdhr >> 8) & 0xff; + buf[6] = (rdhr >> 16) & 0xff; + buf[7] = (rdhr >> 24) & 0xff; +} + +// Return true if more data is available to send or mailboxes are full +int CAN_TxIrq(void) { + int txdata = 1; + + // TODO: We need some kind of error handling? + + while(txdata) { + int mbox = can_find_empty_tx_mbox(); + if(mbox < 0) { + // All mboxes full, wait for next IRQ + return 1; + } + int i=0; + uint8_t databuf[8]; + for (;i<8;i++) + { + if(serial_get_tx_byte(&(databuf[i])) == -1) { + txdata = 0; + break; + } + } + if (i>0) { + can_transmit_mbox(MyCanId+1, mbox, i, databuf); + } + } + return txdata; +} + +void CAN_RxCpltCallback(unsigned int mbox) +{ + CAN_FIFOMailBox_TypeDef* mb = &SOC_CAN->sFIFOMailBox[mbox]; + uint32_t id = (mb->RIR >> CAN_RI0R_STID_Pos) & 0x7FF; + uint8_t dlc = mb->RDTR & CAN_RDT0R_DLC; + uint8_t databuf[8]; + + if(!MyCanId) { // If serial not assigned yet + if(id==PKT_ID_UUID && dlc == 0) { + // Just inform host about my UUID + can_uuid_resp(); + } else if (id == PKT_ID_SET) { + uint8_t short_uuid[SHORT_UUID_LEN]; + pack_uuid(short_uuid); + + // compare my UUID with packet to check if this packet mine + get_rx_data(databuf, mbox); + if (memcmp(&(databuf[2]), short_uuid, SHORT_UUID_LEN) == 0) { + memcpy(&MyCanId, databuf, sizeof(uint16_t)); + /* Set new filter values */ + uint32_t filternbrbitpos = (1U) << CAN_FILTER_NUMBER; + SOC_CAN->FA1R &= ~(filternbrbitpos); + /* Personal ID */ + SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR1 = + ((uint32_t)(MyCanId<<5) << 16U); + /* Catch reset command */ + SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR2 = + ((uint32_t)(PKT_ID_UUID<<5) << 16U); + /* Filter activation */ + SOC_CAN->FA1R |= filternbrbitpos; + /* Leave the initialisation mode for the filter */ + SOC_CAN->FMR &= ~(CAN_FMR_FINIT); + } + } + } else { + if (id == MyCanId) { + // compare my UUID with packet to check if this packet mine + if(dlc == 0) { + // empty packet == ping request + can_transmit(MyCanId+1, 0, NULL); + } else { + get_rx_data(databuf, mbox); + for(int i=0; i < dlc; i++ ) { + serial_rx_byte(databuf[i]); + } + } + } + else if (id == PKT_ID_UUID && dlc > 0) + { + get_rx_data(databuf, mbox); + if (memcmp(databuf, &MyCanId, 2) == 0) + { + // Reset from host + NVIC_SystemReset(); + } + } + } +} + +/** + * @brief This function handles CAN global interrupts + */ +void +CAN_IRQHandler(void) +{ + // RX + if (SOC_CAN->RF0R & CAN_RF0R_FMP0) { + // Mailbox 0 + while(SOC_CAN->RF0R & CAN_RF0R_FMP0) { + CAN_RxCpltCallback(0); + SOC_CAN->RF0R |= CAN_RF0R_RFOM0; + } + } + if (SOC_CAN->RF1R & CAN_RF1R_FMP1) { + // Mailbox 1 + while(SOC_CAN->RF1R & CAN_RF1R_FMP1) { + CAN_RxCpltCallback(1); + SOC_CAN->RF1R |= CAN_RF1R_RFOM1; + } + } + + /* Check Overrun flag for FIFO0 */ + if(SOC_CAN->RF0R & CAN_RF0R_FOVR0) + { + /* Clear FIFO0 Overrun Flag */ + SOC_CAN->RF0R |= CAN_RF0R_FOVR0; + } + /* Check Overrun flag for FIFO1 */ + if(SOC_CAN->RF1R & CAN_RF1R_FOVR1) + { + /* Clear FIFO1 Overrun Flag */ + SOC_CAN->RF1R |= CAN_RF1R_FOVR1; + } + + // TX + if(SOC_CAN->IER & CAN_IER_TMEIE) { // TX IRQ enabled + if(!CAN_TxIrq()) + SOC_CAN->IER &= ~CAN_IER_TMEIE; // Disable TXIRQ + } +} + +static inline const uint32_t +make_btr(uint32_t sjw, // Sync jump width, ... hmm + uint32_t time_seg1, // time segment before sample point, 1 .. 16 + uint32_t time_seg2, // time segment after sample point, 1 .. 8 + uint32_t brp) // Baud rate prescaler, 1 .. 1024 +{ + return + ((uint32_t)(sjw-1)) << CAN_BTR_SJW_Pos + | ((uint32_t)(time_seg1-1)) << CAN_BTR_TS1_Pos + | ((uint32_t)(time_seg2-1)) << CAN_BTR_TS2_Pos + | ((uint32_t)(brp - 1)) << CAN_BTR_BRP_Pos; +} + + +static inline const uint32_t +compute_btr(uint32_t pclock, uint32_t bitrate) { + + /* + Some equations: + Tpclock = 1 / pclock + Tq = brp * Tpclock + Tbs1 = Tq * TS1 + Tbs2 = Tq * TS2 + NominalBitTime = Tq + Tbs1 + Tbs2 + BaudRate = 1/NominalBitTime + + Bit value sample point is after Tq+Tbs1. Ideal sample point + is at 87.5% of NominalBitTime + + Use the lowest brp where ts1 and ts2 are in valid range + */ + + uint32_t bit_clocks = pclock / bitrate; // clock ticks per bit + + uint32_t sjw = 2; + uint32_t qs; + // Find number of time quantas that gives us the exact wanted bit time + for(qs = 18; qs > 9; qs --) { + // check that bit_clocks / quantas is an integer + uint32_t brp_rem = bit_clocks % qs; + if(brp_rem == 0) + break; + } + uint32_t brp = bit_clocks / qs; + uint32_t time_seg2 = qs / 8; // sample at ~87.5% + uint32_t time_seg1 = qs - (1 + time_seg2); + + return make_btr(sjw, time_seg1, time_seg2, brp); +} + +void +can_init(void) +{ + enable_pclock((uint32_t)SOC_CAN); + + gpio_peripheral(GPIO_Rx, CAN_FUNCTION, 1); + gpio_peripheral(GPIO_Tx, CAN_FUNCTION, 0); + + uint32_t pclock = get_pclock_frequency((uint32_t)SOC_CAN); + + uint32_t btr = compute_btr(pclock, CONFIG_SERIAL_BAUD); + + /*##-1- Configure the CAN #######################################*/ + + /* Exit from sleep mode */ + SOC_CAN->MCR &= ~(CAN_MCR_SLEEP); + /* Request initialisation */ + SOC_CAN->MCR |= CAN_MCR_INRQ; + /* Wait the acknowledge */ + while( !(SOC_CAN->MSR & CAN_MSR_INAK) ); + + SOC_CAN->MCR |= MCR_FLAGS; + SOC_CAN->BTR = btr; + + /* Request leave initialisation */ + SOC_CAN->MCR &= ~(CAN_MCR_INRQ); + /* Wait the acknowledge */ + while( SOC_CAN->MSR & CAN_MSR_INAK ); + + /*##-2- Configure the CAN Filter #######################################*/ + uint32_t filternbrbitpos = (1U) << CAN_FILTER_NUMBER; + + /* Select the start slave bank */ + SOC_CAN->FMR |= CAN_FMR_FINIT; + /* Initialisation mode for the filter */ + SOC_CAN->FA1R &= ~(filternbrbitpos); + + SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR1 = + ((uint32_t)(PKT_ID_UUID<<5) << 16U); + SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR2 = + ((uint32_t)(PKT_ID_SET<<5) << 16U); + + /*Identifier list mode for the filter*/ + SOC_CAN->FM1R |= filternbrbitpos; + /* 32-bit scale for the filter */ + SOC_CAN->FS1R |= filternbrbitpos; + + /* FIFO 0 assignation for the filter */ + SOC_CAN->FFA1R &= ~(filternbrbitpos); + + /* Filter activation */ + SOC_CAN->FA1R |= filternbrbitpos; + /* Leave the initialisation mode for the filter */ + SOC_CAN->FMR &= ~(CAN_FMR_FINIT); + + /*##-3- Configure Interrupts #################################*/ + + SOC_CAN->IER |= (CAN_IER_FMPIE0 | CAN_IER_FMPIE1); // RX mailbox IRQ + + armcm_enable_irq(CAN_IRQHandler, CAN_RX0_IRQn, 0); + if(CAN_RX0_IRQn != CAN_RX1_IRQn) + armcm_enable_irq(CAN_IRQHandler, CAN_RX1_IRQn, 0); + if(CAN_RX0_IRQn != CAN_TX_IRQn) + armcm_enable_irq(CAN_IRQHandler, CAN_TX_IRQn, 0); + // TODO: CAN_SCE_IRQ?n + + + /*##-4- Say Hello #################################*/ + can_uuid_resp(); +} +DECL_INIT(can_init); + +void +serial_enable_tx_irq(void) +{ + if(MyCanId == 0) + // Serial port not initialized + return; + + SOC_CAN->IER |= CAN_IER_TMEIE; // TX mailbox IRQ +} diff --git a/src/stm32/stm32f0_can.h b/src/stm32/can.h similarity index 52% rename from src/stm32/stm32f0_can.h rename to src/stm32/can.h index 6b3cb2a4..92429a26 100644 --- a/src/stm32/stm32f0_can.h +++ b/src/stm32/can.h @@ -1,5 +1,5 @@ -#ifndef CORE_INC_CAN_H_ -#define CORE_INC_CAN_H_ +#ifndef __STM32_CAN_H__ +#define __STM32_CAN_H__ // Read UUID (6bytes) #define PKT_ID_UUID (0x321) @@ -8,10 +8,6 @@ // UUID response from slave (6bytes) #define PKT_ID_UUID_RESP (0x323) -#define STM32_UUID_ADDR (0x1FFFF7AC) #define SHORT_UUID_LEN (6) -void CanInit(void); -void CanTransmit(uint32_t id, uint32_t dlc, uint8_t *pkt); - -#endif /* CORE_INC_CAN_H_ */ +#endif /* __STM32_CAN_H__*/ diff --git a/src/stm32/stm32f0.c b/src/stm32/stm32f0.c index 9c536915..a6d5e069 100644 --- a/src/stm32/stm32f0.c +++ b/src/stm32/stm32f0.c @@ -219,7 +219,7 @@ armcm_main(void) // Turn on hsi14 oscillator for ADC hsi14_setup(); - // Support alternate USB/CAN pins on stm32f042 + // Support pin remapping USB/CAN pins on low pinout stm32f042 #ifdef SYSCFG_CFGR1_PA11_PA12_RMP if (CONFIG_STM32F042_PIN_SWAP) { enable_pclock(SYSCFG_BASE); diff --git a/src/stm32/stm32f0_can.c b/src/stm32/stm32f0_can.c deleted file mode 100644 index 2523ba5f..00000000 --- a/src/stm32/stm32f0_can.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Serial over CAN emulation for STM32F042 boards. - * - * Copyright (C) 2019 Eug Krashtan - * This file may be distributed under the terms of the GNU GPLv3 license. - * - */ - -#include "autoconf.h" // CONFIG_SERIAL_BAUD -#include "board/armcm_boot.h" // armcm_enable_irq -#include "board/serial_irq.h" // serial_rx_byte -#include "command.h" // DECL_CONSTANT_STR -#include "internal.h" // enable_pclock -#include "sched.h" // DECL_INIT -#include -#include "stm32f0_can.h" - -DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PA11,PA12"); -#define GPIO_Rx GPIO('A', 11) -#define GPIO_Tx GPIO('A', 12) -#define MCR_FLAGS (CAN_MCR_NART) -#define BTR_FLAGS (CAN_BTR_TS1_2 | CAN_BTR_TS2_0 | /* prescaler */ 11U ) -#define CAN_FILTER_NUMBER 0 -#define CAN_DATA3_Pos 24 -#define CAN_DATA2_Pos 16 -#define CAN_DATA1_Pos 8 -#define CAN_DATA0_Pos 0 - -static uint16_t MyCanId = 0; - -static void can_transmit(uint32_t id, uint32_t dlc, uint8_t *pkt) -{ - /* ToDo: implement serial_get_tx_byte() inside TXComplete interrupt */ - - /* Use only Mailbox 0 to avoid message fragmentation */ - while( !(CAN->TSR & CAN_TSR_TME0) ) /* wait until previous TX completes */ - ; - - /* Set up the Id */ - CAN->sTxMailBox[0].TIR &= CAN_TI1R_TXRQ; - CAN->sTxMailBox[0].TIR |= (id << CAN_TI1R_STID_Pos); - - /* Set up the DLC */ - CAN->sTxMailBox[0].TDTR &= 0xFFFFFFF0U; - CAN->sTxMailBox[0].TDTR |= (dlc & 0xFU); - - /* Set up the data field */ - if(pkt) { - CAN->sTxMailBox[0].TDLR = ((uint32_t)pkt[3] << CAN_DATA3_Pos) | - ((uint32_t)pkt[2] << CAN_DATA2_Pos) | - ((uint32_t)pkt[1] << CAN_DATA1_Pos) | - ((uint32_t)pkt[0] << CAN_DATA0_Pos); - CAN->sTxMailBox[0].TDHR = ((uint32_t)pkt[7] << CAN_DATA3_Pos) | - ((uint32_t)pkt[6] << CAN_DATA2_Pos) | - ((uint32_t)pkt[5] << CAN_DATA1_Pos) | - ((uint32_t)pkt[4] << CAN_DATA0_Pos); - } - - /* Request transmission */ - __sync_synchronize(); // disable write optimization - CAN->sTxMailBox[0].TIR |= CAN_TI1R_TXRQ; -} - -// Convert Unique 96-bit value into 48 bit representation -static void pack_uuid(uint8_t* u) -{ - for(int i=0; isFIFOMailBox[mbox].RDLR >> (i*8)) & 0xFF; - } else { - buf[i] = (CAN->sFIFOMailBox[mbox].RDHR >> ((i-4)*8))& 0xFF; - } - } -} - -void CAN_RxCpltCallback(uint8_t mbox) -{ - uint32_t id = (CAN->sFIFOMailBox[mbox].RIR >> CAN_RI0R_STID_Pos) & 0x7FF; - uint8_t dlc = CAN->sFIFOMailBox[mbox].RDTR & CAN_RDT0R_DLC; - uint8_t databuf[8]; - - if(!MyCanId) { // If serial not assigned yet - if(id==PKT_ID_UUID && dlc == 0) { - // Just inform host about my UUID - can_uuid_resp(); - } else if (id == PKT_ID_SET) { - uint8_t short_uuid[SHORT_UUID_LEN]; - pack_uuid(short_uuid); - - // compare my UUID with packet to check if this packet mine - get_data(databuf, mbox); - if (memcmp(&(databuf[2]), short_uuid, SHORT_UUID_LEN) == 0) { - memcpy(&MyCanId, databuf, sizeof(uint16_t)); - /* Set new filter values */ - uint32_t filternbrbitpos = (1U) << CAN_FILTER_NUMBER; - CAN->FA1R &= ~(filternbrbitpos); - /* Personal ID */ - CAN->sFilterRegister[CAN_FILTER_NUMBER].FR1 = - ((uint32_t)(MyCanId<<5) << 16U); - /* Catch reset command */ - CAN->sFilterRegister[CAN_FILTER_NUMBER].FR2 = - ((uint32_t)(PKT_ID_UUID<<5) << 16U); - /* Filter activation */ - CAN->FA1R |= filternbrbitpos; - /* Leave the initialisation mode for the filter */ - CAN->FMR &= ~(CAN_FMR_FINIT); - } - } - } else { - if (id == MyCanId) { - // compare my UUID with packet to check if this packet mine - if(dlc == 0) { - // empty packet == ping request - can_transmit(MyCanId+1, 0, NULL); - } else { - get_data(databuf, mbox); - for(int i=0; i < dlc; i++ ) { - serial_rx_byte(databuf[i]); - } - } - } - else if (id == PKT_ID_UUID && dlc > 0) - { - get_data(databuf, mbox); - if (memcmp(databuf, &MyCanId, 2) == 0) - { - // Reset from host - NVIC_SystemReset(); - } - } - } -} - -/** - * @brief This function handles HDMI-CEC and CAN global interrupts / - * HDMI-CEC wake-up interrupt through EXTI line 27. - */ -void -CEC_CAN_IRQHandler(void) -{ - if (CAN->RF0R & CAN_RF0R_FMP0) { - // Mailbox 0 - while(CAN->RF0R & CAN_RF0R_FMP0) { - CAN_RxCpltCallback(0); - CAN->RF0R |= CAN_RF0R_RFOM0; - } - } - if (CAN->RF1R & CAN_RF1R_FMP1) { - // Mailbox 1 - while(CAN->RF1R & CAN_RF1R_FMP1) { - CAN_RxCpltCallback(1); - CAN->RF1R |= CAN_RF1R_RFOM1; - } - } - - /* Check Overrun flag for FIFO0 */ - if(CAN->RF0R & CAN_RF0R_FOVR0) - { - /* Clear FIFO0 Overrun Flag */ - CAN->RF0R |= CAN_RF0R_FOVR0; - } - /* Check Overrun flag for FIFO1 */ - if(CAN->RF1R & CAN_RF1R_FOVR1) - { - /* Clear FIFO1 Overrun Flag */ - CAN->RF1R |= CAN_RF1R_FOVR1; - } -} - -void -can_init(void) -{ - enable_pclock((uint32_t)CAN); - gpio_peripheral(GPIO_Rx, GPIO_FUNCTION(4), 1); - gpio_peripheral(GPIO_Tx, GPIO_FUNCTION(4), 0); - - /*##-1- Configure the CAN #######################################*/ - - /* Exit from sleep mode */ - CAN->MCR &= ~(CAN_MCR_SLEEP); - /* Request initialisation */ - CAN->MCR |= CAN_MCR_INRQ; - /* Wait the acknowledge */ - while( !(CAN->MSR & CAN_MSR_INAK) ); - - CAN->MCR |= MCR_FLAGS; - CAN->BTR = BTR_FLAGS; - - /* Request leave initialisation */ - CAN->MCR &= ~(CAN_MCR_INRQ); - /* Wait the acknowledge */ - while( CAN->MSR & CAN_MSR_INAK ); - - /*##-2- Configure the CAN Filter #######################################*/ - uint32_t filternbrbitpos = (1U) << CAN_FILTER_NUMBER; - - /* Select the start slave bank */ - CAN->FMR |= CAN_FMR_FINIT; - /* Initialisation mode for the filter */ - CAN->FA1R &= ~(filternbrbitpos); - - CAN->sFilterRegister[CAN_FILTER_NUMBER].FR1 = - ((uint32_t)(PKT_ID_UUID<<5) << 16U); - CAN->sFilterRegister[CAN_FILTER_NUMBER].FR2 = - ((uint32_t)(PKT_ID_SET<<5) << 16U); - - /*Identifier list mode for the filter*/ - CAN->FM1R |= filternbrbitpos; - /* 32-bit scale for the filter */ - CAN->FS1R |= filternbrbitpos; - - /* FIFO 0 assignation for the filter */ - CAN->FFA1R &= ~(filternbrbitpos); - - /* Filter activation */ - CAN->FA1R |= filternbrbitpos; - /* Leave the initialisation mode for the filter */ - CAN->FMR &= ~(CAN_FMR_FINIT); - - /*##-3- Configure Transmission process #################################*/ - - CAN->IER |= (CAN_IER_FMPIE0 | CAN_IER_FMPIE1); - armcm_enable_irq(CEC_CAN_IRQHandler, CEC_CAN_IRQn, 0); - - /*##-4- Say Hello #################################*/ - can_uuid_resp(); -} -DECL_INIT(can_init); - -void -serial_enable_tx_irq(void) -{ - uint8_t databuf[8]; - if(MyCanId == 0) - // Serial port not initialized - return; - uint8_t txdata =1; - while(txdata) { - int i=0; - for (;i<8;) - { - if(serial_get_tx_byte(&(databuf[i])) == -1) { - txdata = 0; - break; - } - i++; - } - if (i>0) { - can_transmit(MyCanId+1, i, databuf); - } - } -} diff --git a/src/stm32/stm32f1.c b/src/stm32/stm32f1.c index cad28f31..a1c4c7fe 100644 --- a/src/stm32/stm32f1.c +++ b/src/stm32/stm32f1.c @@ -64,6 +64,18 @@ gpio_clock_enable(GPIO_TypeDef *regs) RCC->APB2ENR; } + +static void stm32f1_alternative_remap(uint32_t mapr_mask, uint32_t mapr_value) +{ + // The MAPR register is a mix of write only and r/w bits + // We have to save the written values in a global variable + static uint32_t mapr = 0; + + mapr &= ~mapr_mask; + mapr |= mapr_value; + AFIO->MAPR = mapr; +} + // Set the mode and extended function of a pin void gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup) @@ -105,9 +117,23 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup) if (gpio == GPIO('A', 13) || gpio == GPIO('A', 14)) // Disable SWD to free PA13, PA14 - AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE; + stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk, + AFIO_MAPR_SWJ_CFG_DISABLE); + + // STM32F1 remaps functions to pins in a very different + // way from other STM32s. + // Code below is emulating a few mappings to work like an STM32F4 + uint32_t func = (mode >> 4) & 0xf; + if(( gpio == GPIO('B', 8) || gpio == GPIO('B', 9)) && + func == 9) { // CAN + stm32f1_alternative_remap(AFIO_MAPR_CAN_REMAP_Msk, + AFIO_MAPR_CAN_REMAP_REMAP2); + } + // Add more as needed } + + // Handle USB reboot requests void usb_request_bootloader(void) @@ -180,7 +206,8 @@ armcm_main(void) // Disable JTAG to free PA15, PB3, PB4 enable_pclock(AFIO_BASE); - AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; + stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk, + AFIO_MAPR_SWJ_CFG_JTAGDISABLE); sched_main(); }