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