mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
Diff: targets/TARGET_STM/can_api.c
- Revision:
- 189:f392fc9709a3
- Parent:
- 187:0387e8f68319
--- a/targets/TARGET_STM/can_api.c Thu Nov 08 11:46:34 2018 +0000 +++ b/targets/TARGET_STM/can_api.c Wed Feb 20 22:31:08 2019 +0000 @@ -17,6 +17,495 @@ #if DEVICE_CAN +#ifdef FDCAN1 + +#include "pinmap.h" +#include "PeripheralPins.h" +#include "mbed_error.h" + +static uint32_t can_irq_ids[2] = {0}; +static can_irq_handler irq_handler; + +/** Call all the init functions + * + * @returns + * 0 if mode change failed or unsupported, + * 1 if mode change was successful + */ +int can_internal_init(can_t *obj) +{ + if (HAL_FDCAN_Init(&obj->CanHandle) != HAL_OK) { + error("HAL_FDCAN_Init error\n"); + } + + if (can_filter(obj, 0, 0, CANStandard, 0) == 0) { + error("can_filter error\n"); + } + + if (can_filter(obj, 0, 0, CANExtended, 0) == 0) { + error("can_filter error\n"); + } + + if (HAL_FDCAN_ConfigGlobalFilter(&obj->CanHandle, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK) { + error("HAL_FDCAN_ConfigGlobalFilter error\n"); + } + + if (HAL_FDCAN_Start(&obj->CanHandle) != HAL_OK) { + error("HAL_FDCAN_Start error\n"); + } + + return 1; +} + +void can_init(can_t *obj, PinName rd, PinName td) +{ + /* default frequency is 100 kHz */ + can_init_freq(obj, rd, td, 100000); +} + + +void can_init_freq(can_t *obj, PinName rd, PinName td, int hz) +{ + CANName can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RD); + CANName can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TD); + CANName can = (CANName)pinmap_merge(can_rd, can_td); + MBED_ASSERT((int)can != NC); + + __HAL_RCC_FDCAN_CLK_ENABLE(); + + if (can == CAN_1) { + obj->index = 0; + } +#if defined(FDCAN2_BASE) + else if (can == CAN_2) { + obj->index = 1; + } +#endif + else { + error("can_init wrong instance\n"); + return; + } + + // Select PLL1Q as source of FDCAN clock + RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit; + RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_FDCAN; + RCC_PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PLL; // 10 MHz (RCC_OscInitStruct.PLL.PLLQ = 80) + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) { + error("HAL_RCCEx_PeriphCLKConfig error\n"); + } + + // Configure CAN pins + pinmap_pinout(rd, PinMap_CAN_RD); + pinmap_pinout(td, PinMap_CAN_TD); + // Add pull-ups + if (rd != NC) { + pin_mode(rd, PullUp); + } + if (td != NC) { + pin_mode(td, PullUp); + } + + // Default values + obj->CanHandle.Instance = (FDCAN_GlobalTypeDef *)can; + + /* Bit time parameter + ex with 100 kHz requested frequency hz + fdcan_ker_ck | 10 MHz | 10 MHz + Prescaler | 1 | 1 + Time_quantum (tq) | 100 ns | 100 ns + Bit_rate | 0.1 MBit/s | <hz> + Bit_length | 10 µs = 100 tq | <n_tq> = 10 000 000 / <hz> + Synchronization_segment | 1 tq | 1 tq + Phase_segment_1 | 69 tq | <nts1> = <n_tq> * 0.75 + Phase_segment_2 | 30 tq | <nts2> = <n_tq> - 1 - <nts1> + Synchronization_Jump_width | 30 tq | <nsjw> = <nts2> + */ + int ntq = 10000000 / hz; + + obj->CanHandle.Init.FrameFormat = FDCAN_FRAME_CLASSIC; + obj->CanHandle.Init.Mode = FDCAN_MODE_NORMAL; + obj->CanHandle.Init.AutoRetransmission = ENABLE; + obj->CanHandle.Init.TransmitPause = DISABLE; + obj->CanHandle.Init.ProtocolException = ENABLE; + obj->CanHandle.Init.NominalPrescaler = 1; // Prescaler + obj->CanHandle.Init.NominalTimeSeg1 = ntq * 0.75; // Phase_segment_1 + obj->CanHandle.Init.NominalTimeSeg2 = ntq - 1 - obj->CanHandle.Init.NominalTimeSeg1; // Phase_segment_2 + obj->CanHandle.Init.NominalSyncJumpWidth = obj->CanHandle.Init.NominalTimeSeg2; // Synchronization_Jump_width + obj->CanHandle.Init.DataPrescaler = 0x1; // Not used - only in FDCAN + obj->CanHandle.Init.DataSyncJumpWidth = 0x1; // Not used - only in FDCAN + obj->CanHandle.Init.DataTimeSeg1 = 0x1; // Not used - only in FDCAN + obj->CanHandle.Init.DataTimeSeg2 = 0x1; // Not used - only in FDCAN + obj->CanHandle.Init.MessageRAMOffset = 0; + obj->CanHandle.Init.StdFiltersNbr = 1; // to be aligned with the handle parameter in can_filter + obj->CanHandle.Init.ExtFiltersNbr = 1; // to be aligned with the handle parameter in can_filter + obj->CanHandle.Init.RxFifo0ElmtsNbr = 8; + obj->CanHandle.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_8; + obj->CanHandle.Init.RxFifo1ElmtsNbr = 0; + obj->CanHandle.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8; + obj->CanHandle.Init.RxBuffersNbr = 0; + obj->CanHandle.Init.RxBufferSize = FDCAN_DATA_BYTES_8; + obj->CanHandle.Init.TxEventsNbr = 3; + obj->CanHandle.Init.TxBuffersNbr = 0; + obj->CanHandle.Init.TxFifoQueueElmtsNbr = 3; + obj->CanHandle.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION; + obj->CanHandle.Init.TxElmtSize = FDCAN_DATA_BYTES_8; + + can_internal_init(obj); +} + + +void can_irq_init(can_t *obj, can_irq_handler handler, uint32_t id) +{ + irq_handler = handler; + can_irq_ids[obj->index] = id; +} + +void can_irq_free(can_t *obj) +{ + CANName can = (CANName)obj->CanHandle.Instance; + if (can == CAN_1) { + HAL_NVIC_DisableIRQ(FDCAN1_IT0_IRQn); + HAL_NVIC_DisableIRQ(FDCAN1_IT1_IRQn); + } +#if defined(FDCAN2_BASE) + else if (can == CAN_2) { + HAL_NVIC_DisableIRQ(FDCAN2_IT0_IRQn); + HAL_NVIC_DisableIRQ(FDCAN2_IT1_IRQn); + } +#endif + else { + return; + } + HAL_NVIC_DisableIRQ(FDCAN_CAL_IRQn); + can_irq_ids[obj->index] = 0; +} + +void can_free(can_t *obj) +{ + __HAL_RCC_FDCAN_FORCE_RESET(); + __HAL_RCC_FDCAN_RELEASE_RESET(); + __HAL_RCC_FDCAN_CLK_DISABLE(); +} + + +/** Reset CAN interface. + * + * To use after error overflow. + */ +void can_reset(can_t *obj) +{ + can_mode(obj, MODE_RESET); + HAL_FDCAN_ResetTimeoutCounter(&obj->CanHandle); + HAL_FDCAN_ResetTimestampCounter(&obj->CanHandle); +} + + +int can_frequency(can_t *obj, int f) +{ + if (HAL_FDCAN_Stop(&obj->CanHandle) != HAL_OK) { + error("HAL_FDCAN_Stop error\n"); + } + + /* See can_init_freq function for calculation details */ + int ntq = 10000000 / f; + obj->CanHandle.Init.NominalTimeSeg1 = ntq * 0.75; // Phase_segment_1 + obj->CanHandle.Init.NominalTimeSeg2 = ntq - 1 - obj->CanHandle.Init.NominalTimeSeg1; // Phase_segment_2 + obj->CanHandle.Init.NominalSyncJumpWidth = obj->CanHandle.Init.NominalTimeSeg2; // Synchronization_Jump_width + + return can_internal_init(obj); +} + + +/** Filter out incoming messages + * + * @param obj CAN object + * @param id the id to filter on + * @param mask the mask applied to the id + * @param format format to filter on + * @param handle message filter handle (not supported yet) + * + * @returns + * 0 if filter change failed or unsupported, + * new filter handle if successful (not supported yet => returns 1) + */ +int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle) +{ + UNUSED(handle); // Not supported yet (seems to be a used in read function?) + + FDCAN_FilterTypeDef sFilterConfig = {0}; + + if (format == CANStandard) { + sFilterConfig.IdType = FDCAN_STANDARD_ID; + sFilterConfig.FilterIndex = 0; + sFilterConfig.FilterType = FDCAN_FILTER_MASK; + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; + sFilterConfig.FilterID1 = id; + sFilterConfig.FilterID2 = mask; + } else if (format == CANExtended) { + sFilterConfig.IdType = FDCAN_EXTENDED_ID; + sFilterConfig.FilterIndex = 0; + sFilterConfig.FilterType = FDCAN_FILTER_MASK; + sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; + sFilterConfig.FilterID1 = id; + sFilterConfig.FilterID2 = mask; + } else { // Filter for CANAny format cannot be configured for STM32 + return 0; + } + + if (HAL_FDCAN_ConfigFilter(&obj->CanHandle, &sFilterConfig) != HAL_OK) { + return 0; + } + + return 1; +} + + +int can_write(can_t *obj, CAN_Message msg, int cc) +{ + FDCAN_TxHeaderTypeDef TxHeader = {0}; + + UNUSED(cc); + + // Configure Tx buffer message + TxHeader.Identifier = msg.id; + if (msg.format == CANStandard) { + TxHeader.IdType = FDCAN_STANDARD_ID; + } else { + TxHeader.IdType = FDCAN_EXTENDED_ID; + } + + TxHeader.TxFrameType = FDCAN_DATA_FRAME; + TxHeader.DataLength = msg.len << 16; + TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE; + TxHeader.BitRateSwitch = FDCAN_BRS_OFF; + TxHeader.FDFormat = FDCAN_CLASSIC_CAN; + TxHeader.TxEventFifoControl = FDCAN_STORE_TX_EVENTS; + TxHeader.MessageMarker = 0; + + if (HAL_FDCAN_AddMessageToTxFifoQ(&obj->CanHandle, &TxHeader, msg.data) != HAL_OK) { + // Note for debug: you can get the error code calling HAL_FDCAN_GetError(&obj->CanHandle) + return 0; + } + + return 1; +} + +int can_read(can_t *obj, CAN_Message *msg, int handle) +{ + UNUSED(handle); // Not supported yet (seems to be a handle to a filter configuration?) + + if (HAL_FDCAN_GetRxFifoFillLevel(&obj->CanHandle, FDCAN_RX_FIFO0) == 0) { + return 0; // No message arrived + } + + FDCAN_RxHeaderTypeDef RxHeader = {0}; + if (HAL_FDCAN_GetRxMessage(&obj->CanHandle, FDCAN_RX_FIFO0, &RxHeader, msg->data) != HAL_OK) { + error("HAL_FDCAN_GetRxMessage error\n"); // Should not occur as previous HAL_FDCAN_GetRxFifoFillLevel call reported some data + return 0; + } + + if (RxHeader.IdType == FDCAN_STANDARD_ID) { + msg->format = CANStandard; + } else { + msg->format = CANExtended; + } + msg->id = RxHeader.Identifier; + msg->type = CANData; + msg->len = RxHeader.DataLength >> 16; // see FDCAN_data_length_code value + + return 1; +} + +unsigned char can_rderror(can_t *obj) +{ + FDCAN_ErrorCountersTypeDef ErrorCounters; + + HAL_FDCAN_GetErrorCounters(&obj->CanHandle, &ErrorCounters); + + return (unsigned char)ErrorCounters.RxErrorCnt; +} + +unsigned char can_tderror(can_t *obj) +{ + FDCAN_ErrorCountersTypeDef ErrorCounters; + + HAL_FDCAN_GetErrorCounters(&obj->CanHandle, &ErrorCounters); + + return (unsigned char)ErrorCounters.TxErrorCnt; +} + +void can_monitor(can_t *obj, int silent) +{ + CanMode mode = MODE_NORMAL; + if (silent) { + switch (obj->CanHandle.Init.Mode) { + case FDCAN_MODE_INTERNAL_LOOPBACK: + mode = MODE_TEST_SILENT; + break; + default: + mode = MODE_SILENT; + break; + } + } else { + switch (obj->CanHandle.Init.Mode) { + case FDCAN_MODE_INTERNAL_LOOPBACK: + case FDCAN_MODE_EXTERNAL_LOOPBACK: + mode = MODE_TEST_LOCAL; + break; + default: + mode = MODE_NORMAL; + break; + } + } + + can_mode(obj, mode); +} + +/** Change CAN operation to the specified mode + * + * @param mode The new operation mode (MODE_RESET, MODE_NORMAL, MODE_SILENT, MODE_TEST_LOCAL, MODE_TEST_GLOBAL, MODE_TEST_SILENT) + * + * @returns + * 0 if mode change failed or unsupported, + * 1 if mode change was successful + */ +int can_mode(can_t *obj, CanMode mode) +{ + if (HAL_FDCAN_Stop(&obj->CanHandle) != HAL_OK) { + error("HAL_FDCAN_Stop error\n"); + } + + switch (mode) { + case MODE_RESET: + break; + case MODE_NORMAL: + obj->CanHandle.Init.Mode = FDCAN_MODE_NORMAL; + // obj->CanHandle.Init.NominalPrescaler = 100; // Prescaler + break; + case MODE_SILENT: // Bus Monitoring + obj->CanHandle.Init.Mode = FDCAN_MODE_BUS_MONITORING; + break; + case MODE_TEST_GLOBAL: // External LoopBack + case MODE_TEST_LOCAL: + obj->CanHandle.Init.Mode = FDCAN_MODE_EXTERNAL_LOOPBACK; + break; + case MODE_TEST_SILENT: // Internal LoopBack + obj->CanHandle.Init.Mode = FDCAN_MODE_INTERNAL_LOOPBACK; + // obj->CanHandle.Init.NominalPrescaler = 1; // Prescaler + break; + default: + return 0; + } + + return can_internal_init(obj); +} + + +static void can_irq(CANName name, int id) +{ + FDCAN_HandleTypeDef CanHandle; + CanHandle.Instance = (FDCAN_GlobalTypeDef *)name; + + if (__HAL_FDCAN_GET_IT_SOURCE(&CanHandle, FDCAN_IT_TX_COMPLETE)) { + if (__HAL_FDCAN_GET_FLAG(&CanHandle, FDCAN_FLAG_TX_COMPLETE)) { + __HAL_FDCAN_CLEAR_FLAG(&CanHandle, FDCAN_FLAG_TX_COMPLETE); + irq_handler(can_irq_ids[id], IRQ_TX); + } + } + + if (__HAL_FDCAN_GET_IT_SOURCE(&CanHandle, FDCAN_IT_RX_BUFFER_NEW_MESSAGE)) { + if (__HAL_FDCAN_GET_FLAG(&CanHandle, FDCAN_IT_RX_BUFFER_NEW_MESSAGE)) { + __HAL_FDCAN_CLEAR_FLAG(&CanHandle, FDCAN_IT_RX_BUFFER_NEW_MESSAGE); + irq_handler(can_irq_ids[id], IRQ_RX); + } + } + + if (__HAL_FDCAN_GET_IT_SOURCE(&CanHandle, FDCAN_IT_ERROR_WARNING)) { + if (__HAL_FDCAN_GET_FLAG(&CanHandle, FDCAN_FLAG_ERROR_WARNING)) { + __HAL_FDCAN_CLEAR_FLAG(&CanHandle, FDCAN_FLAG_ERROR_WARNING); + irq_handler(can_irq_ids[id], IRQ_ERROR); + } + } + + if (__HAL_FDCAN_GET_IT_SOURCE(&CanHandle, FDCAN_IT_ERROR_PASSIVE)) { + if (__HAL_FDCAN_GET_FLAG(&CanHandle, FDCAN_FLAG_ERROR_PASSIVE)) { + __HAL_FDCAN_CLEAR_FLAG(&CanHandle, FDCAN_FLAG_ERROR_PASSIVE); + irq_handler(can_irq_ids[id], IRQ_PASSIVE); + } + } + + if (__HAL_FDCAN_GET_IT_SOURCE(&CanHandle, FDCAN_IT_BUS_OFF)) { + if (__HAL_FDCAN_GET_FLAG(&CanHandle, FDCAN_FLAG_BUS_OFF)) { + __HAL_FDCAN_CLEAR_FLAG(&CanHandle, FDCAN_FLAG_BUS_OFF); + irq_handler(can_irq_ids[id], IRQ_BUS); + } + } +} + +void FDCAN1_IT0_IRQHandler(void) +{ + can_irq(CAN_1, 0); +} + +void FDCAN1_IT1_IRQHandler(void) +{ + can_irq(CAN_1, 0); +} + +void FDCAN2_IT0_IRQHandler(void) +{ + can_irq(CAN_2, 1); +} + +void FDCAN2_IT1_IRQHandler(void) +{ + can_irq(CAN_2, 1); +} + +// TODO Add other interrupts ? +void can_irq_set(can_t *obj, CanIrqType type, uint32_t enable) +{ + uint32_t interrupts = 0; + + switch (type) { + case IRQ_TX: + interrupts = FDCAN_IT_TX_COMPLETE; + break; + case IRQ_RX: + interrupts = FDCAN_IT_RX_BUFFER_NEW_MESSAGE; + break; + case IRQ_ERROR: + interrupts = FDCAN_IT_ERROR_WARNING; + break; + case IRQ_PASSIVE: + interrupts = FDCAN_IT_ERROR_PASSIVE; + break; + case IRQ_BUS: + interrupts = FDCAN_IT_BUS_OFF; + default: + return; + } + + if (enable) { + HAL_FDCAN_ActivateNotification(&obj->CanHandle, interrupts, 0); + } else { + HAL_FDCAN_DeactivateNotification(&obj->CanHandle, interrupts); + } + + NVIC_SetVector(FDCAN1_IT0_IRQn, (uint32_t)&FDCAN1_IT0_IRQHandler); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_SetVector(FDCAN1_IT1_IRQn, (uint32_t)&FDCAN1_IT1_IRQHandler); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); +#if defined(FDCAN2_BASE) + NVIC_SetVector(FDCAN2_IT0_IRQn, (uint32_t)&FDCAN2_IT0_IRQHandler); + NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + NVIC_SetVector(FDCAN2_IT1_IRQn, (uint32_t)&FDCAN2_IT1_IRQHandler); + NVIC_EnableIRQ(FDCAN2_IT1_IRQn); +#endif +} + +#else /* FDCAN1 */ + #include "cmsis.h" #include "pinmap.h" #include "PeripheralPins.h" @@ -454,8 +943,6 @@ int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle) { - int retval = 0; - // filter for CANAny format cannot be configured for STM32 if ((format == CANStandard) || (format == CANExtended)) { CAN_FilterConfTypeDef sFilterConfig; @@ -480,9 +967,9 @@ sFilterConfig.BankNumber = 14 + handle; HAL_CAN_ConfigFilter(&obj->CanHandle, &sFilterConfig); - retval = handle; } - return retval; + + return 1; } static void can_irq(CANName name, int id) @@ -716,4 +1203,6 @@ NVIC_EnableIRQ(irq_n); } +#endif /* FDCAN1 */ + #endif // DEVICE_CAN