diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5de44fc..3f8fc86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${COMMON_LINKER_FLAGS}")
project(bobbycar-controller-firmware ASM C CXX)
-add_definitions(-DUSE_HAL_DRIVER -DSTM32F103xE)
+add_definitions(-DUSE_HAL_DRIVER -DSTM32F103xE -DFEATURE_CAN)
include_directories(
.
@@ -54,6 +54,7 @@ add_executable(firmware.elf
STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_adc.c
STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c
STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c
+ STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_can.c
bobbycar-foc-model/BLDC_controller.h
bobbycar-foc-model/BLDC_controller.c
@@ -68,6 +69,8 @@ add_executable(firmware.elf
config.h
defines.h
main.cpp
+ can.c
+ can_feedc0de.cpp
)
add_custom_command(OUTPUT firmware.hex
diff --git a/can.c b/can.c
new file mode 100644
index 0000000..581c42b
--- /dev/null
+++ b/can.c
@@ -0,0 +1,369 @@
+#include "stm32f1xx_hal.h"
+
+#include "can_feedc0de.h"
+#include "can.h"
+
+/* Definition for CANx clock resources */
+#define CANx CAN1
+#define CANx_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE()
+#define CANx_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
+
+#define CANx_FORCE_RESET() __HAL_RCC_CAN1_FORCE_RESET()
+#define CANx_RELEASE_RESET() __HAL_RCC_CAN1_RELEASE_RESET()
+
+/* Definition for CANx Pins */
+#define CANx_TX_PIN GPIO_PIN_9
+#define CANx_TX_GPIO_PORT GPIOB
+#define CANx_RX_PIN GPIO_PIN_8
+#define CANx_RX_GPIO_PORT GPIOB
+
+/* Definition for CANx AFIO Remap */
+#define CANx_AFIO_REMAP_CLK_ENABLE() __HAL_RCC_AFIO_CLK_ENABLE()
+#define CANx_AFIO_REMAP_RX_TX_PIN() __HAL_AFIO_REMAP_CAN1_2()
+
+/* Definition for CAN's NVIC */
+#define CANx_RX_IRQn USB_LP_CAN1_RX0_IRQn
+#define CANx_RX_IRQHandler USB_LP_CAN1_RX0_IRQHandler
+#define CANx_TX_IRQn USB_HP_CAN1_TX_IRQn
+#define CANx_TX_IRQHandler USB_HP_CAN1_TX_IRQHandler
+
+
+/**
+ ******************************************************************************
+ * @file CAN/CAN_Networking/Src/main.c
+ * @author MCD Application Team
+ * @brief This example shows how to configure the CAN peripheral
+ * to send and receive CAN frames in normal mode. The sent frames
+ * are used to control Leds by pressing KEY Push Button.
+ ******************************************************************************
+ * @attention
+ *
+ *
© Copyright (c) 2016 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Private variables ---------------------------------------------------------*/
+uint8_t ubKeyNumber = 0x0;
+CAN_HandleTypeDef CanHandle;
+CAN_TxHeaderTypeDef TxHeader;
+CAN_RxHeaderTypeDef RxHeader;
+uint8_t TxData[8];
+uint8_t RxData[8];
+uint32_t TxMailbox;
+
+/* Private function prototypes -----------------------------------------------*/
+static void __NO_RETURN Error_Handler(void);
+static void CAN_MspInit(CAN_HandleTypeDef *hcan);
+static void CAN_MspDeInit(CAN_HandleTypeDef *hcan);
+static void CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan);
+static void CAN_TxMailboxCompleteCallback(CAN_HandleTypeDef *hcan);
+
+/* Private functions ---------------------------------------------------------*/
+
+static uint32_t len_to_dlc(uint8_t len)
+{
+ uint32_t len_u32 = len;
+
+ if (len_u32 <= 8)
+ return len_u32;
+
+ // CAN-FD-only lengths currently not supported
+ Error_Handler();
+}
+
+static uint8_t dlc_to_len(uint32_t dlc)
+{
+ // Check if DLC valid
+ if (dlc & ~0xFu)
+ Error_Handler();
+
+ // CAN-FD-only lengths currently not supported
+ if (dlc > 8)
+ Error_Handler();
+
+ return dlc;
+}
+
+/**
+ * @brief Main program.
+ * @param None
+ * @retval None
+ */
+void can_test(void)
+{
+ TxHeader.DLC = 2;
+ TxHeader.StdId = 0x321;
+
+ /* Set the data to be transmitted */
+ TxData[0] = ubKeyNumber + 1;
+ TxData[1] = 0xAD;
+
+ /* Start the Transmission process */
+ if (HAL_CAN_AddTxMessage(&CanHandle, &TxHeader, TxData, &TxMailbox) != HAL_OK)
+ {
+ /* Transmission request Error */
+ Error_Handler();
+ }
+}
+
+void can_tx(uint16_t id, const uint8_t* data, uint8_t len)
+{
+ TxHeader.StdId = id;
+ TxHeader.DLC = len_to_dlc(len);
+
+ while (HAL_CAN_GetTxMailboxesFreeLevel(&CanHandle) < 1)
+ {
+ }
+
+ /* Start the Transmission process */
+ if (HAL_CAN_AddTxMessage(&CanHandle, &TxHeader, (uint8_t*)data, &TxMailbox) != HAL_OK)
+ {
+ /* Transmission request Error */
+ Error_Handler();
+ }
+}
+
+/**
+ * @brief This function is executed in case of error occurrence.
+ * @param None
+ * @retval None
+ */
+static void __NO_RETURN Error_Handler(void)
+{
+ while (1)
+ {
+ }
+}
+
+/**
+ * @brief Configures the CAN.
+ * @param None
+ * @retval None
+ */
+void can_config(void)
+{
+ CAN_FilterTypeDef sFilterConfig;
+
+ /* Configure the CAN peripheral */
+ CanHandle.Instance = CANx;
+
+ CanHandle.MspInitCallback = CAN_MspInit;
+ CanHandle.MspDeInitCallback = CAN_MspDeInit;
+
+ CanHandle.Init.TimeTriggeredMode = DISABLE;
+ CanHandle.Init.AutoBusOff = ENABLE;
+ CanHandle.Init.AutoWakeUp = DISABLE;
+ CanHandle.Init.AutoRetransmission = ENABLE;
+ CanHandle.Init.ReceiveFifoLocked = DISABLE;
+ CanHandle.Init.TransmitFifoPriority = DISABLE;
+ CanHandle.Init.Mode = CAN_MODE_NORMAL;
+ CanHandle.Init.SyncJumpWidth = CAN_SJW_1TQ;
+ CanHandle.Init.TimeSeg1 = CAN_BS1_6TQ;
+ CanHandle.Init.TimeSeg2 = CAN_BS2_5TQ;
+ CanHandle.Init.Prescaler = 4;
+
+ if (HAL_CAN_Init(&CanHandle) != HAL_OK)
+ {
+ /* Initialization Error */
+ Error_Handler();
+ }
+
+ /* Configure the CAN Filter */
+ sFilterConfig.FilterBank = 0;
+ sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
+ sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
+ sFilterConfig.FilterIdHigh = 0x0000;
+ sFilterConfig.FilterIdLow = 0x0310;
+ sFilterConfig.FilterMaskIdHigh = 0x0000;
+ sFilterConfig.FilterMaskIdLow = 0x07F8;
+ sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
+ sFilterConfig.FilterActivation = ENABLE;
+ sFilterConfig.SlaveStartFilterBank = 14;
+
+ if (HAL_CAN_ConfigFilter(&CanHandle, &sFilterConfig) != HAL_OK)
+ {
+ /* Filter configuration Error */
+ Error_Handler();
+ }
+
+ if (HAL_CAN_RegisterCallback(&CanHandle, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID, CAN_RxFifo0MsgPendingCallback) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ if (HAL_CAN_RegisterCallback(&CanHandle, HAL_CAN_TX_MAILBOX0_COMPLETE_CB_ID, CAN_TxMailboxCompleteCallback) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ if (HAL_CAN_RegisterCallback(&CanHandle, HAL_CAN_TX_MAILBOX1_COMPLETE_CB_ID, CAN_TxMailboxCompleteCallback) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ if (HAL_CAN_RegisterCallback(&CanHandle, HAL_CAN_TX_MAILBOX2_COMPLETE_CB_ID, CAN_TxMailboxCompleteCallback) != HAL_OK)
+ {
+ Error_Handler();
+ }
+
+ /* Start the CAN peripheral */
+ if (HAL_CAN_Start(&CanHandle) != HAL_OK)
+ {
+ /* Start Error */
+ Error_Handler();
+ }
+
+ /* Activate CAN RX notification */
+ if (HAL_CAN_ActivateNotification(&CanHandle, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
+ {
+ /* Notification Error */
+ Error_Handler();
+ }
+
+ /* Activate CAN TX notification */
+ if (HAL_CAN_ActivateNotification(&CanHandle, CAN_IT_TX_MAILBOX_EMPTY) != HAL_OK)
+ {
+ /* Notification Error */
+ Error_Handler();
+ }
+
+ /* Configure Transmission process */
+ TxHeader.StdId = 0x321;
+ TxHeader.ExtId = 0x01;
+ TxHeader.RTR = CAN_RTR_DATA;
+ TxHeader.IDE = CAN_ID_STD;
+ TxHeader.DLC = 2;
+ TxHeader.TransmitGlobalTime = DISABLE;
+}
+
+/**
+ * @brief Rx Fifo 0 message pending callback in non blocking mode
+ * @param CanHandle: pointer to a CAN_HandleTypeDef structure that contains
+ * the configuration information for the specified CAN.
+ * @retval None
+ */
+static void CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *CanHandle)
+{
+ /* Get RX message */
+ if (HAL_CAN_GetRxMessage(CanHandle, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
+ {
+ /* Reception Error */
+ Error_Handler();
+ }
+
+ if (RxHeader.IDE == CAN_ID_STD &&
+ (RxHeader.StdId == CAN_ID_COMMAND_STW_TO_BACK ||
+ RxHeader.StdId == CAN_ID_FEEDBACK_STW_TO_BACK))
+ {
+ can_feedc0de_handle_frame(RxHeader.StdId, RxData, dlc_to_len(RxHeader.DLC));
+ }
+
+ // slightly yucky, but we don't want to block inside the IRQ handler
+ if (HAL_CAN_GetTxMailboxesFreeLevel(CanHandle) >= 2)
+ {
+ can_feedc0de_poll();
+ }
+}
+
+static void CAN_TxMailboxCompleteCallback(CAN_HandleTypeDef *hcan)
+{
+ // slightly yucky, but we don't want to block inside the IRQ handler
+ if (HAL_CAN_GetTxMailboxesFreeLevel(hcan) >= 2)
+ {
+ can_feedc0de_poll();
+ }
+}
+
+/**
+ * @brief CAN MSP Initialization
+ * This function configures the hardware resources used in this example:
+ * - Peripheral's clock enable
+ * - Peripheral's GPIO Configuration
+ * - NVIC configuration for DMA interrupt request enable
+ * @param hcan: CAN handle pointer
+ * @retval None
+ */
+static void CAN_MspInit(CAN_HandleTypeDef *hcan)
+{
+ GPIO_InitTypeDef GPIO_InitStruct;
+
+ /*##-1- Enable peripherals and GPIO Clocks #################################*/
+ /* CAN1 Periph clock enable */
+ CANx_CLK_ENABLE();
+ /* Enable GPIO clock ****************************************/
+ CANx_GPIO_CLK_ENABLE();
+ /* Enable AFIO clock and Remap CAN PINs to PB8 and PB9*******/
+ CANx_AFIO_REMAP_CLK_ENABLE();
+ CANx_AFIO_REMAP_RX_TX_PIN();
+
+ /*##-2- Configure peripheral GPIO ##########################################*/
+ /* CAN1 TX GPIO pin configuration */
+ GPIO_InitStruct.Pin = CANx_TX_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ GPIO_InitStruct.Pull = GPIO_PULLUP;
+
+ HAL_GPIO_Init(CANx_TX_GPIO_PORT, &GPIO_InitStruct);
+
+ /* CAN1 RX GPIO pin configuration */
+ GPIO_InitStruct.Pin = CANx_RX_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+ GPIO_InitStruct.Pull = GPIO_PULLUP;
+
+ HAL_GPIO_Init(CANx_RX_GPIO_PORT, &GPIO_InitStruct);
+
+ /*##-3- Configure the NVIC #################################################*/
+ /* NVIC configuration for CAN1 Reception complete interrupt */
+ HAL_NVIC_SetPriority(CANx_RX_IRQn, 1, 0);
+ HAL_NVIC_EnableIRQ(CANx_RX_IRQn);
+
+ HAL_NVIC_SetPriority(CANx_TX_IRQn, 1, 0);
+ HAL_NVIC_EnableIRQ(CANx_TX_IRQn);
+}
+
+/**
+ * @brief CAN MSP De-Initialization
+ * This function frees the hardware resources used in this example:
+ * - Disable the Peripheral's clock
+ * - Revert GPIO to their default state
+ * @param hcan: CAN handle pointer
+ * @retval None
+ */
+static void CAN_MspDeInit(CAN_HandleTypeDef *hcan)
+{
+ /*##-1- Reset peripherals ##################################################*/
+ CANx_FORCE_RESET();
+ CANx_RELEASE_RESET();
+
+ /*##-2- Disable peripherals and GPIO Clocks ################################*/
+ /* De-initialize the CAN1 TX GPIO pin */
+ HAL_GPIO_DeInit(CANx_TX_GPIO_PORT, CANx_TX_PIN);
+ /* De-initialize the CAN1 RX GPIO pin */
+ HAL_GPIO_DeInit(CANx_RX_GPIO_PORT, CANx_RX_PIN);
+
+ /*##-4- Disable the NVIC for CAN reception #################################*/
+ HAL_NVIC_DisableIRQ(CANx_RX_IRQn);
+}
+
+/**
+* @brief This function handles CAN1 RX0 interrupt request.
+* @param None
+* @retval None
+*/
+void CANx_RX_IRQHandler(void)
+{
+ HAL_CAN_IRQHandler(&CanHandle);
+}
+
+void CANx_TX_IRQHandler(void)
+{
+ HAL_CAN_IRQHandler(&CanHandle);
+}
diff --git a/can.h b/can.h
new file mode 100644
index 0000000..02ad8f4
--- /dev/null
+++ b/can.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#define CAN_ID_COMMAND_STW_TO_BACK 0x311
+#define CAN_ID_COMMAND_BACK_TO_STW 0x312
+#define CAN_ID_COMMAND_STW_TO_FRONT 0x315
+#define CAN_ID_COMMAND_FRONT_TO_STW 0x316
+
+#define CAN_ID_FEEDBACK_STW_TO_BACK 0x319
+#define CAN_ID_FEEDBACK_BACK_TO_STW 0x31A
+#define CAN_ID_FEEDBACK_STW_TO_FRONT 0x31D
+#define CAN_ID_FEEDBACK_FRONT_TO_STW 0x31E
+
+#define CAN_ID_HB_BACK_TO_STW 0x331
+#define CAN_ID_HB_FRONT_TO_STW 0x333
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void can_config(void);
+void can_test(void);
+void can_tx(uint16_t id, const uint8_t* data, uint8_t len);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/can_fc.h b/can_fc.h
new file mode 100644
index 0000000..179c093
--- /dev/null
+++ b/can_fc.h
@@ -0,0 +1,216 @@
+#pragma once
+
+#ifdef __cplusplus
+#include
+#include
+#include
+#include
+
+#include "can.h"
+
+//#define FC_STRICT
+
+#define FC_PROTOCOL_ERROR()
+#define Error_Handler() while(1)
+
+constexpr const uint8_t FC_CHUNK_SIZE = 7;
+constexpr const uint8_t FC_TAG_INIT = 0x45;
+constexpr const uint8_t FC_TAG_END = 0x7d;
+
+static uint8_t next_tag(uint8_t tag)
+{
+ if (tag == 0)
+ {
+ return FC_TAG_INIT;
+ }
+ else
+ {
+ return tag + 0x04;
+ }
+}
+
+class FCSender
+{
+public:
+ FCSender(uint16_t id) : id(id)
+ {
+ }
+
+ bool tx_pending()
+ {
+ return pos_sent != len && pos_sent == pos_acked;
+ }
+
+ bool transfer_in_progress()
+ {
+ return data != nullptr && pos_acked != len;
+ }
+
+ bool transfer_finished()
+ {
+ return data != nullptr && pos_acked == len;
+ }
+
+ void reset(const uint8_t* data, size_t len)
+ {
+ pos_sent = 0;
+ pos_acked = 0;
+ tag_sent = 0;
+
+ this->data = data;
+ this->len = len;
+ }
+
+ void handle_frame(const uint8_t* payload, uint8_t payload_len)
+ {
+ if (pos_sent == 0)
+ return;
+
+ if (payload_len != 1)
+ return;
+
+ if (payload[0] != tag_sent)
+ return;
+
+ pos_acked = pos_sent;
+ }
+
+ void tx()
+ {
+ uint8_t payload[1 + FC_CHUNK_SIZE];
+
+ // Still waiting for ack, or done transmitting
+ if (!tx_pending())
+ return;
+
+ size_t new_pos_sent = std::min(pos_sent + FC_CHUNK_SIZE, len);
+ uint8_t sent_size = new_pos_sent - pos_sent;
+ uint8_t new_tag_sent = next_tag(tag_sent);
+ if (new_tag_sent == FC_TAG_END)
+ Error_Handler();
+
+ payload[0] = new_tag_sent;
+ memcpy(&payload[1], data + pos_sent, sent_size);
+ can_tx(id, payload, 1 + sent_size);
+
+ pos_sent = new_pos_sent;
+ tag_sent = new_tag_sent;
+ }
+
+private:
+ uint16_t id;
+ const uint8_t* data;
+ size_t len;
+ size_t pos_sent;
+ size_t pos_acked;
+ uint8_t tag_sent;
+};
+
+class FCReceiver
+{
+public:
+ FCReceiver(uint16_t id, uint8_t* data, size_t len) : id(id)
+ {
+ reset(data, len);
+ }
+
+ bool ack_pending()
+ {
+ return pos_received != pos_acked;
+ }
+
+ bool transfer_in_progress()
+ {
+ return ready && data != nullptr && pos_acked != len;
+ }
+
+ bool transfer_finished()
+ {
+ return ready && data != nullptr && pos_acked == len;
+ }
+
+ void reset(uint8_t* data, size_t len)
+ {
+ // Poor man's mutex
+ ready = false;
+
+ pos_received = 0;
+ pos_acked = 0;
+ tag_expected = next_tag(0);
+
+ this->data = data;
+ this->len = len;
+
+ ready = true;
+ }
+
+ void handle_frame(const uint8_t* payload, uint8_t payload_len)
+ {
+ if (!transfer_in_progress())
+ return;
+
+ if (tag_expected == FC_TAG_END)
+ return;
+
+ if (payload_len < 1)
+ return;
+
+ // Reset receiver if frame with FC_TAG_INIT received when expecting different one
+ if (payload[0] == FC_TAG_INIT && tag_expected != FC_TAG_INIT)
+ reset(data, len);
+
+ // Don't accept new data if we haven't acked the previous data yet
+ if (ack_pending())
+ return;
+
+ uint8_t data_len = payload_len - 1;
+ if (data_len > len - pos_received)
+ return;
+
+ // Ignore all other tags
+ if (payload[0] != tag_expected)
+ return;
+
+ memcpy(&data[pos_received], &payload[1], data_len);
+
+ pos_received += data_len;
+ }
+
+ void ack()
+ {
+ uint8_t payload[1];
+
+ if (!ack_pending())
+ return;
+
+ // Poor man's mutex
+ ready = false;
+
+ // ISR may have called reset() in the mean time
+ if (!ack_pending())
+ {
+ ready = true;
+ return;
+ }
+
+ payload[0] = tag_expected;
+ can_tx(id, payload, sizeof(payload));
+
+ pos_acked = pos_received;
+ tag_expected = next_tag(tag_expected);
+
+ ready = true;
+ }
+
+private:
+ bool ready;
+
+ uint16_t id;
+ uint8_t* data;
+ size_t len;
+ size_t pos_received;
+ size_t pos_acked;
+ uint8_t tag_expected;
+};
+
+#endif // __cplusplus
diff --git a/can_feedc0de.cpp b/can_feedc0de.cpp
new file mode 100644
index 0000000..7f30651
--- /dev/null
+++ b/can_feedc0de.cpp
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+
+#include "protocol.h"
+#include "can_fc.h"
+
+#include "can_feedc0de.h"
+
+
+static_assert((sizeof(Command) + FC_CHUNK_SIZE - 1) / FC_CHUNK_SIZE < 15);
+
+void CANFeedc0de::poll()
+{
+ if (feedc0de_fcs.tx_pending())
+ feedc0de_fcs.tx();
+
+ if (feedc0de_fcr.ack_pending())
+ feedc0de_fcr.ack();
+}
+
+void CANFeedc0de::send_feedback(const Feedback& in)
+{
+ feedback = in;
+ feedc0de_fcs.reset(((uint8_t*)&feedback) + 2, sizeof(feedback) - 4);
+}
+
+bool CANFeedc0de::get_command(Command& out)
+{
+ if (!feedc0de_fcr.transfer_finished())
+ return false;
+
+ out = command;
+ feedc0de_fcr.reset(((uint8_t*)&command) + 2, sizeof(command) - 4);
+
+ return true;
+}
+
+void CANFeedc0de::handle_frame(uint16_t id, uint8_t* frame, uint8_t len)
+{
+ switch (id)
+ {
+ case CAN_ID_FEEDBACK_STW_TO_BACK:
+ feedc0de_fcs.handle_frame(frame, len);
+ break;
+ case CAN_ID_COMMAND_STW_TO_BACK:
+ feedc0de_fcr.handle_frame(frame, len);
+ break;
+ default:
+ Error_Handler();
+ }
+}
+
+extern "C"
+{
+
+void can_feedc0de_handle_frame(uint16_t id, uint8_t* frame, uint8_t len)
+{
+ extern CANFeedc0de can_feedc0de;
+ can_feedc0de.handle_frame(id, frame, len);
+}
+
+void can_feedc0de_poll()
+{
+ extern CANFeedc0de can_feedc0de;
+ can_feedc0de.poll();
+}
+
+}
diff --git a/can_feedc0de.h b/can_feedc0de.h
new file mode 100644
index 0000000..f80cf39
--- /dev/null
+++ b/can_feedc0de.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#ifdef __cplusplus
+#include
+#include "protocol.h"
+#include "can_fc.h"
+class CANFeedc0de
+{
+public:
+ void poll();
+ void handle_frame(uint16_t id, uint8_t* frame, uint8_t len);
+ void send_feedback(const Feedback& in);
+ bool get_command(Command& out);
+private:
+ Command command;
+ Feedback feedback;
+
+ FCSender feedc0de_fcs = FCSender(CAN_ID_FEEDBACK_BACK_TO_STW);
+ FCReceiver feedc0de_fcr = FCReceiver(CAN_ID_COMMAND_BACK_TO_STW, ((uint8_t *)&command) + 2, sizeof(command) - 4);
+};
+
+extern "C"
+{
+#endif
+
+void can_feedc0de_handle_frame(uint16_t id, uint8_t* frame, uint8_t len);
+void can_feedc0de_poll();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/main.cpp b/main.cpp
index 6673bc8..11f7f63 100644
--- a/main.cpp
+++ b/main.cpp
@@ -34,6 +34,14 @@ extern "C" {
extern const P rtP_Left; // default settings defined in BLDC_controller_data.c
}
+#ifdef FEATURE_CAN
+#include "can.h"
+#include "can_feedc0de.h"
+
+CANFeedc0de can_feedc0de;
+
+#endif
+
namespace {
TIM_HandleTypeDef htim_right;
TIM_HandleTypeDef htim_left;
@@ -104,8 +112,6 @@ struct {
Command command;
Feedback feedback;
-
-
void filtLowPass32(int16_t u, uint16_t coef, int32_t *y);
void SystemClock_Config();
@@ -222,6 +228,11 @@ int main()
int pwm = 0;
int8_t dir = 1;
#else
+
+ can_config();
+ MODIFY_REG(RCC->CR, RCC_CR_HSITRIM, (0x1aU << RCC_CR_HSITRIM_Pos));
+
+#ifndef FEATURE_CAN
HAL_UART_Receive_DMA(&huart2, (uint8_t *)&command, sizeof(command));
#endif
@@ -265,6 +276,7 @@ int main()
board_temp_deg_c = (TEMP_CAL_HIGH_DEG_C - TEMP_CAL_LOW_DEG_C) * (board_temp_adcFilt - TEMP_CAL_LOW_ADC) / (TEMP_CAL_HIGH_ADC - TEMP_CAL_LOW_ADC) + TEMP_CAL_LOW_DEG_C;
sendFeedback();
+ can_feedc0de.poll();
#ifdef FEATURE_BUTTON
if (HAL_GPIO_ReadPin(BUTTON_PORT, BUTTON_PIN))
@@ -1049,6 +1061,9 @@ void parseCommand()
{
bool any_parsed{false};
+#ifdef FEATURE_CAN
+ any_parsed = can_feedc0de.get_command(command);
+#else
for (int i = 0; i < 1; i++)
{
if (command.start != Command::VALID_HEADER)
@@ -1058,6 +1073,13 @@ void parseCommand()
if (command.checksum != checksum)
continue;
+ any_parsed = true;
+ break;
+ }
+#endif
+
+ if (any_parsed)
+ {
left.state = command.left;
right.state = command.right;
@@ -1070,12 +1092,8 @@ void parseCommand()
command.start = Command::INVALID_HEADER; // Change the Start Frame for timeout detection in the next cycle
timeoutCntSerial = 0; // Reset the timeout counter
-
- any_parsed = true;
- break;
}
-
- if (!any_parsed)
+ else
{
if (timeoutCntSerial++ >= 100) // Timeout qualification
{
@@ -1087,58 +1105,69 @@ void parseCommand()
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
+#ifndef FEATURE_CAN
// Check periodically the received Start Frame. Try to re-sync by reseting the DMA
if (main_loop_counter % 25 == 0)
{
HAL_UART_DMAStop(&huart2);
HAL_UART_Receive_DMA(&huart2, (uint8_t *)&command, sizeof(command));
}
+#endif
}
}
}
void sendFeedback()
{
- if (main_loop_counter % 50 == 0) { // Send data periodically
- if(UART_DMA_CHANNEL->CNDTR == 0) {
- feedback.start = Feedback::VALID_HEADER;
+ // Send data periodically
+ if (main_loop_counter % 50 != 0)
+ return;
- feedback.left.angle = left.rtY.a_elecAngle;
- feedback.right.angle = right.rtY.a_elecAngle;
+#ifndef FEATURE_CAN
+ if (UART_DMA_CHANNEL->CNDTR != 0)
+ return;
+#endif
- feedback.left.speed = left.rtY.n_mot;
- feedback.right.speed = right.rtY.n_mot;
+ feedback.start = Feedback::VALID_HEADER;
- feedback.left.error = left.rtY.z_errCode;
- feedback.right.error = right.rtY.z_errCode;
+ feedback.left.angle = left.rtY.a_elecAngle;
+ feedback.right.angle = right.rtY.a_elecAngle;
- feedback.left.current = left.rtU.i_DCLink;
- feedback.right.current = right.rtU.i_DCLink;
+ feedback.left.speed = left.rtY.n_mot;
+ feedback.right.speed = right.rtY.n_mot;
- feedback.left.chops = left.chops;
- feedback.right.chops = right.chops;
- left.chops = 0;
- right.chops = 0;
+ feedback.left.error = left.rtY.z_errCode;
+ feedback.right.error = right.rtY.z_errCode;
- feedback.left.hallA = left.rtU.b_hallA;
- feedback.left.hallB = left.rtU.b_hallB;
- feedback.left.hallC = left.rtU.b_hallC;
- feedback.right.hallA = right.rtU.b_hallA;
- feedback.right.hallB = right.rtU.b_hallB;
- feedback.right.hallC = right.rtU.b_hallC;
+ feedback.left.current = left.rtU.i_DCLink;
+ feedback.right.current = right.rtU.i_DCLink;
- feedback.batVoltage = batVoltage * BAT_CALIB_REAL_VOLTAGE / BAT_CALIB_ADC;
- feedback.boardTemp = board_temp_deg_c;
- feedback.timeoutCntSerial = timeoutCntSerial;
+ feedback.left.chops = left.chops;
+ feedback.right.chops = right.chops;
+ left.chops = 0;
+ right.chops = 0;
- feedback.checksum = calculateChecksum(feedback);
+ feedback.left.hallA = left.rtU.b_hallA;
+ feedback.left.hallB = left.rtU.b_hallB;
+ feedback.left.hallC = left.rtU.b_hallC;
+ feedback.right.hallA = right.rtU.b_hallA;
+ feedback.right.hallB = right.rtU.b_hallB;
+ feedback.right.hallC = right.rtU.b_hallC;
- UART_DMA_CHANNEL->CCR &= ~DMA_CCR_EN;
- UART_DMA_CHANNEL->CNDTR = sizeof(feedback);
- UART_DMA_CHANNEL->CMAR = uint64_t(&feedback);
- UART_DMA_CHANNEL->CCR |= DMA_CCR_EN;
- }
- }
+ feedback.batVoltage = batVoltage * BAT_CALIB_REAL_VOLTAGE / BAT_CALIB_ADC;
+ feedback.boardTemp = board_temp_deg_c;
+ feedback.timeoutCntSerial = timeoutCntSerial;
+
+ feedback.checksum = calculateChecksum(feedback);
+
+#ifdef FEATURE_CAN
+ can_feedc0de.send_feedback(feedback);
+#else
+ UART_DMA_CHANNEL->CCR &= ~DMA_CCR_EN;
+ UART_DMA_CHANNEL->CNDTR = sizeof(feedback);
+ UART_DMA_CHANNEL->CMAR = uint64_t(&feedback);
+ UART_DMA_CHANNEL->CCR |= DMA_CCR_EN;
+#endif
}
} // anonymous namespace
diff --git a/stm32f1xx_hal_conf.h b/stm32f1xx_hal_conf.h
index 42311da..8a3b39b 100644
--- a/stm32f1xx_hal_conf.h
+++ b/stm32f1xx_hal_conf.h
@@ -36,7 +36,7 @@ extern "C" {
*/
#define HAL_MODULE_ENABLED
#define HAL_ADC_MODULE_ENABLED
-/* #define HAL_CAN_MODULE_ENABLED */
+#define HAL_CAN_MODULE_ENABLED
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
/* #define HAL_CEC_MODULE_ENABLED */
#define HAL_CORTEX_MODULE_ENABLED
@@ -129,7 +129,7 @@ extern "C" {
#define PREFETCH_ENABLE 1U
#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */
-#define USE_HAL_CAN_REGISTER_CALLBACKS 0U /* CAN register callback disabled */
+#define USE_HAL_CAN_REGISTER_CALLBACKS 1U /* CAN register callback disabled */
#define USE_HAL_CEC_REGISTER_CALLBACKS 0U /* CEC register callback disabled */
#define USE_HAL_DAC_REGISTER_CALLBACKS 0U /* DAC register callback disabled */
#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */