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 <glpontus@gmail.com>
This commit is contained in:
bondus 2020-06-25 00:59:38 +02:00 committed by GitHub
parent 7cab732ae9
commit 7a8e9591e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 493 additions and 279 deletions

View File

@ -159,18 +159,36 @@ config USBSERIAL
bool "Use USB for communication (instead of serial)" bool "Use USB for communication (instead of serial)"
depends on HAVE_STM32_USBFS || HAVE_STM32_USBOTG depends on HAVE_STM32_USBFS || HAVE_STM32_USBOTG
default y default y
config CANSERIAL config CANSERIAL
bool "Use CAN for communication (instead of serial)" bool "Use CAN for communication (instead of serial)"
depends on MACH_STM32F042 && !USBSERIAL depends on !USBSERIAL
default n default n
config SERIAL_BAUD config SERIAL_BAUD
int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANSERIAL int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANSERIAL
default 500000 if 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 config STM32F042_PIN_SWAP
bool "Use PA9/PA10 for USB or CAN" if MACH_STM32F042 bool "Use PA9/PA10 for USB or CAN" if MACH_STM32F042
depends on (USBSERIAL || CANSERIAL) && MACH_STM32F042 depends on (USBSERIAL || CANSERIAL) && MACH_STM32F042
default y if (USBSERIAL || CANSERIAL) default y if (USBSERIAL || CANSERIAL)
default n default n
help
Remaps logical pins PA11/PA12 to physical PA9/PA10 on low pincount F042 devices.
config SERIAL config SERIAL
depends on !USBSERIAL && !CANSERIAL depends on !USBSERIAL && !CANSERIAL
bool bool

View File

@ -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-y := stm32/serial.c
serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c
src-$(CONFIG_SERIAL) += $(serial-src-y) generic/serial_irq.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 src-$(CONFIG_CANSERIAL) += $(can-src-y) generic/serial_irq.c
# Binary output file rules # Binary output file rules

440
src/stm32/can.c Normal file
View File

@ -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
}

View File

@ -1,5 +1,5 @@
#ifndef CORE_INC_CAN_H_ #ifndef __STM32_CAN_H__
#define CORE_INC_CAN_H_ #define __STM32_CAN_H__
// Read UUID (6bytes) // Read UUID (6bytes)
#define PKT_ID_UUID (0x321) #define PKT_ID_UUID (0x321)
@ -8,10 +8,6 @@
// UUID response from slave (6bytes) // UUID response from slave (6bytes)
#define PKT_ID_UUID_RESP (0x323) #define PKT_ID_UUID_RESP (0x323)
#define STM32_UUID_ADDR (0x1FFFF7AC)
#define SHORT_UUID_LEN (6) #define SHORT_UUID_LEN (6)
void CanInit(void); #endif /* __STM32_CAN_H__*/
void CanTransmit(uint32_t id, uint32_t dlc, uint8_t *pkt);
#endif /* CORE_INC_CAN_H_ */

View File

@ -219,7 +219,7 @@ armcm_main(void)
// Turn on hsi14 oscillator for ADC // Turn on hsi14 oscillator for ADC
hsi14_setup(); 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 #ifdef SYSCFG_CFGR1_PA11_PA12_RMP
if (CONFIG_STM32F042_PIN_SWAP) { if (CONFIG_STM32F042_PIN_SWAP) {
enable_pclock(SYSCFG_BASE); enable_pclock(SYSCFG_BASE);

View File

@ -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);
}
}
}

View File

@ -64,6 +64,18 @@ gpio_clock_enable(GPIO_TypeDef *regs)
RCC->APB2ENR; 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 // Set the mode and extended function of a pin
void void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup) 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)) if (gpio == GPIO('A', 13) || gpio == GPIO('A', 14))
// Disable SWD to free PA13, PA14 // 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 // Handle USB reboot requests
void void
usb_request_bootloader(void) usb_request_bootloader(void)
@ -180,7 +206,8 @@ armcm_main(void)
// Disable JTAG to free PA15, PB3, PB4 // Disable JTAG to free PA15, PB3, PB4
enable_pclock(AFIO_BASE); 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(); sched_main();
} }