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:
parent
7cab732ae9
commit
7a8e9591e3
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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_ */
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue