aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/stm32/Kconfig20
-rw-r--r--src/stm32/Makefile2
-rw-r--r--src/stm32/can.c440
-rw-r--r--src/stm32/can.h (renamed from src/stm32/stm32f0_can.h)10
-rw-r--r--src/stm32/stm32f0.c2
-rw-r--r--src/stm32/stm32f0_can.c267
-rw-r--r--src/stm32/stm32f1.c31
7 files changed, 493 insertions, 279 deletions
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 <eug.krashtan@gmail.com>
+ * Copyright (C) 2020 Pontus Borg <glpontus@gmail.com>
+ * 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 <string.h>
+#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; i<SHORT_UUID_LEN; i++) {
+ u[i] = *((uint8_t*)(UID_BASE+i)) ^
+ *((uint8_t*)(UID_BASE+i+SHORT_UUID_LEN));
+ }
+}
+
+static void can_uuid_resp(void)
+{
+ uint8_t short_uuid[SHORT_UUID_LEN];
+ pack_uuid(short_uuid);
+ can_transmit(PKT_ID_UUID_RESP, SHORT_UUID_LEN, short_uuid);
+}
+
+static void get_rx_data(uint8_t* buf, unsigned int mbox)
+{
+ uint32_t rdlr = SOC_CAN->sFIFOMailBox[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
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 <eug.krashtan@gmail.com>
- * 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 <string.h>
-#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; i<SHORT_UUID_LEN; i++) {
- u[i] = *((uint8_t*)(STM32_UUID_ADDR+i)) ^
- *((uint8_t*)(STM32_UUID_ADDR+i+SHORT_UUID_LEN));
- }
-}
-
-static void can_uuid_resp(void)
-{
- uint8_t short_uuid[SHORT_UUID_LEN];
- pack_uuid(short_uuid);
- can_transmit(PKT_ID_UUID_RESP, SHORT_UUID_LEN, short_uuid);
-}
-
-static void get_data(uint8_t* buf, uint8_t mbox)
-{
- for(int i=0; i < 8; i++ ) {
- if (i<4) {
- buf[i] = (CAN->sFIFOMailBox[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();
}