APM32E103 CAN总线双向通信开发指南(拿走不谢)
1. 硬件连接 1.1 硬件要求 - 两块APM32E103开发板 - CAN收发器(如TJA1050) 2 - 120Ω终端电阻 2 - 杜邦线若干 1.2 连接方式 ``` APM32E103 Board A <--> APM32E103 Board B CAN_TX -------- CAN_TX CAN_RX -------- CAN_RX GND -------- GND ``` 1.3 注意事项 1. 在总线两端各接一个120Ω电阻用于阻抗匹配 2. 确保两个开发板共地 3. 建议使用示波器或逻辑分析仪观察CAN总线波形 2. 软件配置 2.1 环境准备 - STM32CubeIDE - APm32 HAL库 2.2 CAN初始化代码 ```c // CAN初始化函数 void MX_CAN_Init(void) { hcan.Instance = CAN1; hcan.Init.Prescaler = 16; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = DISABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = DISABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } } ``` 2.3 CAN过滤器配置 ```c // CAN过滤器配置 void CAN_FilterConfig(void) { CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) { Error_Handler(); } // 启动CAN if (HAL_CAN_Start(&hcan) != HAL_OK) { Error_Handler(); } // 激活CAN RX中断 if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) { Error_Handler(); } } ``` 3. 通信实现 3.1 发送数据函数 ```c // CAN数据发送函数 HAL_StatusTypeDef CAN_SendData(uint8_t data, uint8_t size) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailbox; TxHeader.StdId = 0x123; // 标准ID TxHeader.ExtId = 0x00; // 扩展ID TxHeader.RTR = CAN_RTR_DATA; TxHeader.IDE = CAN_ID_STD; TxHeader.DLC = size; TxHeader.TransmitGlobalTime = DISABLE; return HAL_CAN_AddTxMessage(&hcan, &TxHeader, data, &TxMailbox); } ``` 3.2 接收数据函数 ```c // CAN数据接收函数 HAL_StatusTypeDef CAN_ReceiveData(uint8_t data, uint8_t size) { CAN_RxHeaderTypeDef RxHeader; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, data) == HAL_OK) { size = RxHeader.DLC; return HAL_OK; } return HAL_ERROR; } ``` 3.3 中断回调函数 ```c // CAN接收中断回调函数 void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef hcan) { uint8_t receivedData[8]; uint8_t size; if (CAN_ReceiveData(receivedData, &size) == HAL_OK) { // 处理接收到的数据 // 例如: 根据接收到的数据控制LED或发送回复 // 示例: 收到数据后发送回复 uint8_t replyData[] = {0xAA, 0xBB, 0xCC}; CAN_SendData(replyData, sizeof(replyData)); } } ``` 4. 主程序实现 4.1 主函数 ```c int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_CAN_Init(); CAN_FilterConfig(); uint8_t buttonState = 0; uint8_t sendData[] = {0x01, 0x02, 0x03}; uint8_t receiveData[8]; uint8_t receiveSize; while (1) { // 检测按钮按下 if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET) { HAL_Delay(50); // 消抖 if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_RESET) { buttonState = 1; } } // 按钮按下时发送数据 if (buttonState == 1) { if (CAN_SendData(sendData, sizeof(sendData)) == HAL_OK) { buttonState = 0; HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 发送成功指示灯 } } // 非阻塞接收数据 if (CAN_ReceiveData(receiveData, &receiveSize) == HAL_OK) { // 处理接收到的数据 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 接收成功指示灯 } HAL_Delay(10); } } ``` 5. 调试与测试 5.1 常见问题排查 1. 通信失败: - 检查硬件连接是否正确 - 确认终端电阻已正确连接 - 检查波特率设置是否一致 2. 数据丢失: - 增加接收缓冲区大小 - 优化中断优先级 - 检查总线负载是否过高 3. 信号质量问题: - 使用示波器检查信号完整性 - 确保总线终端电阻匹配 - 检查电源稳定性 5.2 测试流程 1. 单独测试每个开发板的CAN发送功能 2. 测试单向通信(板A发送,板B接收) 3. 测试双向通信 4. 测试按钮触发通信功能 5. 测试长时间通信稳定性 6. 注意事项 1. 总线拓扑: - 使用线性总线拓扑,避免星形连接 - 总线长度不宜过长(建议小于40米) 2. 信号完整性: - 使用双绞线连接 - 避免与高频信号线平行走线 3. 软件设计: - 添加超时机制,避免总线故障导致程序卡死 - 实现错误检测和恢复机制 - 考虑添加心跳包检测通信状态 4. 性能优化: - 根据实际需求调整CAN波特率 - 合理设置过滤器减少不必要的中断 - 优化数据处理流程 通过以上配置和代码实现,两个APM32E103开发板可以通过CAN总线建立可靠的双向通信,并通过按钮触发数据交换。
|