stm32: STM32F0 CAN feature

Signed-off-by: Eug Krashtan <eug.krashtan@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Eug Krashtan 2019-12-28 13:32:43 +02:00 committed by Kevin O'Connor
parent 5238c3f872
commit a34dbc7029
4 changed files with 290 additions and 0 deletions

View File

@ -135,9 +135,13 @@ config CANSERIAL
bool "Use CAN for communication (instead of serial)"
depends on MACH_STM32F042 && !USBSERIAL
default n
config SERIAL_BAUD
int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANSERIAL
default 500000 if CANSERIAL
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
config SERIAL
depends on !USBSERIAL && !CANSERIAL

View File

@ -41,6 +41,8 @@ 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
src-$(CONFIG_CANSERIAL) += $(can-src-y) generic/serial_irq.c
# Binary output file rules
target-y += $(OUT)klipper.bin

267
src/stm32/stm32f0_can.c Normal file
View File

@ -0,0 +1,267 @@
/*
* 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);
}
}
}

17
src/stm32/stm32f0_can.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef CORE_INC_CAN_H_
#define CORE_INC_CAN_H_
// Read UUID (6bytes)
#define PKT_ID_UUID (0x321)
// Set address (2bytes) to UUID (6b)
#define PKT_ID_SET (0x322)
// 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_ */