STM32CubeMX_环境搭建_GPIO_外部中断
STM32CubeMX_定时器中断_PWM
STM32CubeMX_UART_printf_接收中断_DMA空闲中断_LPUART
STM32CubeMX_以太网_RMII_LwIP_UDP
前四节简单的总结了GPIO, EXTI, TIMER, UART, 以太网的相关用法, 本节总结一下STM32的CAN, FDCAN, CAN3的用法. STM32的CAN很多系列都有: F0/1/2/3/4/7
, L4/L4+
等都能找到CAN的身影, 分别有1路到3路不等. 而FDCAN
在STM32G系列
/H系列
/L5系列
中比较常见. 本节我们先从CAN开始, 原理性的东西自己查去, 主要看操作.
用的是下面这个自制的STM32F405RGT6
的板子:
CAN的引脚为:
CAN | STM32 |
---|---|
CAN1_TX | PB9 |
CAN1_RX | PB8 |
CAN2_TX | PB13 |
CAN2_RX | PB12 |
然后通过CAN电平转换芯片TJA1051
连接到CAN分析仪上去:
MCU本身外接的12MHz的晶振, 硬件连接介绍完毕, 下面开始操作.
步骤如下:
STM32CubeMX
, 点击 ACCESS TO MCU SELECTOR
, 选择 STM32F405RGTx
Pinout & Configuration
-> System Core
-> SYS
-> Debug
选择 Serial Wire
Pinout & Configuration
-> System Core
-> RCC
-> HSE
选择 Crystal/Ceramic Resonator
注意CAN时钟
属于APB1 peripheral clocks(MHz)
, 所以是42MHz
.
Pinout & Configuration
-> Connectivity
-> CAN1
, Mode
里面勾选Master Mode
, 发现默认的CAN引脚是PA11, PA12
, 我们修改为PB8, PB9
:
Pinout & Configuration
-> Connectivity
-> CAN2
, 勾选Slave Mode
, 默认的就是PB12, PB13
, 不用手动修正:
CAN1
和 CAN2
的 Configuration
中, 预分配Prescaler
设置为7, BS1
设为5
, BS2
设为6
, 这样波特率为42M/7/(5+6+1)=500K
, 1
是固定的, 与RJW
的关系不大:
NVIC Setting
中勾选CAN1, CAN2的RX0
中断, 优先级默认不改:
Project Manager
-> Project
-> Browse
选择工程位置(Project Location
), 填入工程名(Project Name
), Toolchain/IDE
选择 MDK-ARM
.
Project Manager
-> Code Generator
-> 勾选Copy only the necessary library files
, 还有Generate peripheral initialization as a pair of .c/.h files per periphral
点击右上角 GENERATE CODE
按钮生成代码, 打开工程.
Keil 点击魔术棒或者Project
-> Options for Target ...
, 默认配置Debug
为ST-link Debugger
, 点击Setting
-> Flash Download
-> 勾选Reset and Run
, 这样下载后可以自动复位运行.
如有疑问, 仍然去参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.
CAN1为例, main.c
中添加:
/* USER CODE BEGIN PV */
CAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0};
uint32_t TxMailbox;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
Error_Handler();
}
TxHeader.StdId = 0x321;
TxHeader.ExtId = 0x01;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.IDE = CAN_ID_STD;
TxHeader.DLC = 2;
TxHeader.TransmitGlobalTime = DISABLE;
TxData[0] = 0xAC;
TxData[1] = 0xAD;
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
++TxData[1];
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
HAL_Delay(10);
}
/* USER CODE END 3 */
CAN接收需要先设置滤波器, 这里设为0, 全部接收:
/* USER CODE BEGIN 0 */
void CAN1_Config(void) {
/*## Configure the CAN Filter ##*/
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(&hcan1, &sFilterConfig) != HAL_OK) {
Error_Handler(); /* Filter configuration Error */
}
/*## Start the CAN peripheral ##*/
if (HAL_CAN_Start(&hcan1) != HAL_OK) {
Error_Handler();
}
/*## Activate CAN RX notification ##*/
if (HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
void CAN2_Config(void) {
/*## Configure the CAN Filter ##*/
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 14;
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(&hcan2, &sFilterConfig) != HAL_OK) {
Error_Handler(); /* Filter configuration Error */
}
/*## Start the CAN peripheral ##*/
if (HAL_CAN_Start(&hcan2) != HAL_OK) {
Error_Handler();
}
/*## Activate CAN RX notification ##*/
if (HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
/* USER CODE END 0 */
上面CAN2
的FilterBank
是从14开始的, 一共28个滤波器, CAN1
和CAN2
各占14个.
main函数中初始化调用:
/* USER CODE BEGIN 2 */
CAN1_Config();
CAN2_Config();
/* USER CODE END 2 */
然后是接收中断, 这里把接收到的中断通通回传, CAN1的传给CAN1, CAN2的回传给CAN2:
/* USER CODE BEGIN 4 */
/**
* @brief Rx Fifo 0 message pending callback
* @param hcan: pointer to a CAN_HandleTypeDef structure that contains
* the configuration information for the specified CAN.
* @retval None
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK) {
Error_Handler();
}
TxHeader.StdId = RxHeader.StdId;
TxHeader.ExtId = RxHeader.ExtId;
TxHeader.RTR = RxHeader.RTR;
TxHeader.IDE = RxHeader.IDE;
TxHeader.DLC = RxHeader.DLC;
memcpy(TxData, RxData, RxHeader.DLC);
HAL_CAN_AddTxMessage(hcan, &TxHeader, TxData, &TxMailbox);
}
编译下载运行:
事实上, 我们上面没有考虑发送失败怎么办, 可以自己写一个发送函数, 发送失败延时重发, 失败超过限定次数放弃:
void CAN_SendStdMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *txMsg, uint8_t sendData[]) {
//txMsg->StdId = 0x321;
txMsg->ExtId = 0x00;
txMsg->RTR = CAN_RTR_DATA;
txMsg->IDE = CAN_ID_STD;
txMsg->DLC = 8;
txMsg->TransmitGlobalTime = DISABLE;
uint32_t TxMailbox;
uint32_t j = 0;
while(1) {
if ((HAL_CAN_AddTxMessage(hcan, txMsg, sendData, &TxMailbox)!= HAL_ERROR) || (j++ > 10)) {
break; //success or fail_num>10, stop send
}
for(__IO uint16_t k=0; k < 500; k++); //busy, delay some time
}
}
void CAN_SendExtMsg(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *txMsg, uint8_t sendData[]) {
txMsg->StdId = 0x00;
//txMsg->ExtId = 0x00;
txMsg->RTR = CAN_RTR_DATA;
txMsg->IDE = CAN_ID_EXT;
txMsg->DLC = 8;
txMsg->TransmitGlobalTime = DISABLE;
uint32_t TxMailbox;
uint32_t j = 0;
while(1) {
if ((HAL_CAN_AddTxMessage(hcan, txMsg, sendData, &TxMailbox)!= HAL_ERROR) || (j++ > 10)) {
break; //success or fail_num>10, stop send
}
for(__IO uint16_t k=0; k < 500; k++); //busy, delay some time
}
}
STM32F413 / 423, STM32F765 / 767 / 769 / 777 / 779
系列有第三路CAN, 也就是CAN3
, 上面CAN1
是Master
, CAN2
是Slave
, 28个滤波器组也用完了, 那CAN3
怎么搞? 以STM32F767为例:
图中直接变成一个Master, 应该是可以和CAN1平起平坐的, 中断里面也没什么差别:
CAN3
的滤波器设置与CAN1
类似:
void CAN3_Config(void) {
/*## Configure the CAN Filter ##*/
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;
if (HAL_CAN_ConfigFilter(&hcan3, &sFilterConfig) != HAL_OK) {
Error_Handler(); /* Filter configuration Error */
}
/*## Start the CAN peripheral ##*/
if (HAL_CAN_Start(&hcan3) != HAL_OK) {
Error_Handler();
}
/*## Activate CAN RX notification ##*/
if (HAL_CAN_ActivateNotification(&hcan3, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK) {
Error_Handler();
}
}
接收中断函数也是一样的, 不必担心.
打开HAL_CAN_ConfigFilter
函数定义, 里面有这几句话:
/* CAN1 and CAN2 are dual instances with 28 common filters banks */
/* Select master instance to access the filter banks */
/* CAN3 is single instance with 14 dedicated filters banks */
CAN3有自己独立的滤波器. 但HAL库函数用起来, 让你感觉和CAN1用法是一样的.
上面说到, STM32G系列
/H系列
/L5系列
有CANFD, 换一块自己手打的STM32G474
的小板子:
FDCAN电平芯片在背面, 注意用到FDCAN功能时, CAN电平转换芯片也需要支持才行, 硬件连接上:
FDCAN | STM32 |
---|---|
FDCAN1_TX | PD1 |
FDCAN1_RX | PD0 |
FDCAN2_TX | PB6 |
FDCAN2_RX | PB5 |
FDCAN3_TX | PB4 |
FDCAN3_RX | PB3 |
外挂的12MHz晶振.
操作如下:
STM32CubeMX
, 点击 ACCESS TO MCU SELECTOR
, 选择 STM32G474VETx
Pinout & Configuration
-> System Core
-> SYS
-> Debug
选择 Serial Wire
Pinout & Configuration
-> System Core
-> RCC
-> HSE
选择 Crystal/Ceramic Resonator
Pinout & Configuration
-> Connectivity
-> FDCAN1
, Mode
可选Classic Master
作为普通CAN
用, 也可以选择FD
, 由于我最近没有CANFD
的应用, 就选Classic Master
先当普通CAN
用(至于FD
的可参考http://bbs.eeworld.com.cn/thread-1080702-1-1.html这篇文章), 然后手动把引脚从PA11/PA12
挪到PD0/PD1
:
回到 Clock Configuration
时钟树里, 可以看到FDCAN
的时钟是独立可选的, 先用默认的PCLK1, 170MHz:
CANFD的速率可变, 仲裁比特率最高1Mbps(与CAN相同), 数据比特率可以很高(8Mbps?),这就牵涉到两个波特率的配置, 如下图所示:
红色框中的Nominal
可以当成仲裁波特率, 蓝色框中的Data
当成数据波特率, 由于这里只当CAN
用, 设成一样的500K波特率就可以了, 计算方法: FDCAN_CLK / Prescaler / (seg1 + seg2 + 1) = 170M/17/(9+10+1)=500K
.
NVIC
中勾选FDCAN1 interrupt 0
, 优先级先默认不配置:
参考上面的生成代码
一小节, 打开工程.
main.c
代码如下:
/* USER CODE BEGIN PV */
FDCAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8] = {0};
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
TxHeader.Identifier = 0x1234567;
TxHeader.IdType = FDCAN_EXTENDED_ID;
TxHeader.TxFrameType = FDCAN_DATA_FRAME;
TxHeader.DataLength = FDCAN_DLC_BYTES_8; //not 8
TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
TxHeader.BitRateSwitch = FDCAN_BRS_OFF;
TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;
++TxData[7];
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK) {
Error_Handler();
}
HAL_Delay(1000);
}
/* USER CODE END 3 */
main.c
代码如下, 滤波器设置了全接收(标准帧未测试), 中断函数中回传接收到的CAN帧:
/* USER CODE BEGIN PV */
FDCAN_TxHeaderTypeDef TxHeader;
FDCAN_RxHeaderTypeDef RxHeader;
uint8_t TxData[8] = {0};
uint8_t RxData[8];
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
void FDCAN1_Config(void) {
FDCAN_FilterTypeDef sFilterConfig;
/* Configure Rx filter */
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x00;
sFilterConfig.FilterID2 = 0x7FF;
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
sFilterConfig.IdType = FDCAN_EXTENDED_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x00;
sFilterConfig.FilterID2 = 0x1FFFFFFF;
if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
/* Configure global filter:
Filter all remote frames with STD and EXT ID
Reject non matching frames with STD ID and EXT ID */
if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK)
{
Error_Handler();
}
/* Start the FDCAN module */
if (HAL_FDCAN_Start(&hfdcan1) != HAL_OK)
{
Error_Handler();
}
if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
FDCAN1_Config();
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
{
/* Retrieve Rx messages from RX FIFO0 */
if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
{
Error_Handler();
if(hfdcan == &hfdcan1) {
TxHeader.Identifier = RxHeader.Identifier;
TxHeader.IdType = RxHeader.IdType; //FDCAN_STANDARD_ID, FDCAN_EXTENDED_ID
TxHeader.TxFrameType = RxHeader.RxFrameType; //FDCAN_DATA_FRAME, FDCAN_REMOTE_FRAME
TxHeader.DataLength = RxHeader.DataLength; //FDCAN_DLC_BYTES_8
TxHeader.ErrorStateIndicator = RxHeader.ErrorStateIndicator; //FDCAN_ESI_ACTIVE
TxHeader.BitRateSwitch = RxHeader.BitRateSwitch; //FDCAN_BRS_OFF
TxHeader.FDFormat = RxHeader.FDFormat; //FDCAN_CLASSIC_CAN, FDCAN_FD_CAN
TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
TxHeader.MessageMarker = 0;
memcpy(TxData, RxData, TxHeader.DataLength);
if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData) != HAL_OK) {
Error_Handler();
}
}
}
}
}
/* USER CODE END 4 */
https://download.csdn.net/download/weifengdq/11967173
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息: