diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index 6bfc15e2..c29ad205 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -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 diff --git a/src/stm32/Makefile b/src/stm32/Makefile index 3a063402..e5627ec4 100644 --- a/src/stm32/Makefile +++ b/src/stm32/Makefile @@ -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 diff --git a/src/stm32/stm32f0_can.c b/src/stm32/stm32f0_can.c new file mode 100644 index 00000000..2523ba5f --- /dev/null +++ b/src/stm32/stm32f0_can.c @@ -0,0 +1,267 @@ +/* + * Serial over CAN emulation for STM32F042 boards. + * + * Copyright (C) 2019 Eug Krashtan + * 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 +#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; isFIFOMailBox[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/stm32f0_can.h b/src/stm32/stm32f0_can.h new file mode 100644 index 00000000..6b3cb2a4 --- /dev/null +++ b/src/stm32/stm32f0_can.h @@ -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_ */