STM32F103VET6的CAN通信代码分享
STM32F103VET6单片机之间的CAN通信+筛选器配置(白名单模式)。
CAN_Filter_0 : 16位列表模式,配置4个标准帧ID;
CAN_Filter_1 : 32位列表模式,配置2个扩展帧ID;
CAN_Filter_2 : 32位列表模式,配置2个扩展帧ID;
其它10个筛选器未使用。
参考的资料
- 《零死角玩转STM32—F429.pdf》,可以去野火电子论坛下载
- STM32标准库官方demo《STM32F10x_StdPeriph_Lib_V3.5.0.rar》,可以去st.com直接搜索“STSW-STM32054”或者复制以下链接访问,前提是要注册一个账号才能下载
https://www.st.com/content/st_com/en/products/embedded-software/mcu-mpu-embedded-software/stm32-embedded-software/stm32-standard-peripheral-libraries/stsw-stm32054.html
说明
CAN的相关介绍,请自行查找资料,本次分享不做介绍;请自行阅读上述参考的资料相关内容,个人认为讲解的比较全面。
CAN通信须满足的条件
- 硬件没问题;
- 且两块板子断电后,CAH-H 和CAN-L总线之间的阻值须保证在60欧母左右;
- 其次两个单片机的CAN外设必须配置正确,一旦有一个单片机的CAN没有配置,就不会通信正常。
代码的主要功能
- 单片机A和单片机B的代码完全一样,都是间隔500ms发送一帧数据,数据有8帧标准帧和8帧扩展帧;且发送成功后,运行灯会亮50ms;
- 在CAN接收中断服务函数内,先判断接收数据属于标准帧还是扩展帧,接着通过串口打印接收数据桢的ID;
- printf函数映射到串口3;
- 控制运行灯的GPIO为PB11,低电平点亮LED;
- 以上3、4可以根据实际情况自行更改;
代码
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
#include "stm32f10x_conf.h"
#include "stdio.h"
#define RUN_LED_ON GPIOB->BRR = GPIO_Pin_11
#define RUN_LED_OFF GPIOB->BSRR = GPIO_Pin_11
const uint32_t ul_CAN_ID_table[16] = { 0x100,
0x101,
0x102,
0x103,
0x104,
0x105,
0x106,
0x107,
0x18880000,
0x18880001,
0x18880002,
0x18880003,
0x18880004,
0x18880005,
0x18880006,
0x18880007};
uint8_t uc_temp_table[8];
void systick_config(void)
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
}
void SysTick_Delay_ms(uint32_t ticks)//16777215 16777215 / 9000 = 1864.135
{
if(ticks > 1864)
ticks = 1864;
SysTick->LOAD = ticks * 9000 - 1; /* set reload register */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* Enable SysTick Timer down counter */
do
{
ticks = SysTick->CTRL;
}
while((ticks & 0x01)&&!(ticks & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; /* Stop the SysTick Timer(counter disabled) */
SysTick->VAL = 0;
}
void gpio_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOD Periph clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* Configure output pushpull mode */
/* PB11 RUN_LED */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*
pin55 PD8 FSMC_D13 USART3_TX(remap)
pin56 PD9 FSMC_D14 USART3_RX(remap)
*/
void USART3_config(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/* Configure USARTy Rx as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure USARTy Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_BaudRate = 2000000;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART3,&USART_InitStructure);
USART_Cmd(USART3,ENABLE);
}
void USARTx_Send_Single_Byte(USART_TypeDef* USARTx,uint8_t Temp_data)
{
USART_ClearFlag(USARTx,USART_FLAG_TC);
USART_SendData(USARTx,Temp_data);
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
}
/*
功能:can1配置
500kbps,其它波特率下面有介绍
筛选器用了0、1、2,三个都配置为白名单模式,未使用掩码模式,只接收白名单的帧
筛选器0:16位白名单模式,配置了4个白名单标准帧ID
筛选器1:32位白名单模式,配置了2个白名单扩展帧ID
筛选器2:32位白名单模式,配置了2个白名单扩展帧ID
CAN1有两个接收FIFO(FIFO0 和 FIFO1),本程序只使用了FIFO0;三个筛选器都映射到FIFO0
程序最前面定义了16个ID,其中8个标准帧ID,8个扩展帧ID
*/
void can_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Configure CAN1 IOs **********************************************/
/* GPIOD and AFIO clocks enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE);
/* CAN1 Periph clocks enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/* Configure CAN1 RX pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure CAN1 TX pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Remap CAN1 GPIOs */
GPIO_PinRemapConfig(GPIO_Remap2_CAN1 , ENABLE);
/* Configure CAN1 **************************************************/
/* CAN1 register init */
CAN_DeInit(CAN1);
/* Struct init*/
CAN_StructInit(&CAN_InitStructure);
/* CAN1 cell init */
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = ENABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;//reSynchronization Jump Width
CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
/*CLK_APB1 = 36MHz
ss = 1 bs1 = 3 bs2 = 2
bit time width is 6(1 + 3 + 2)
baudrate period is tq*6
36MHz / 6 = 6MHz
6MHz / CAN_Prescaler
= 6MHz / 12
= 500KHz
*/
/*
1 Tq = 1/(36MHz / CAN_Prescaler)
= 1/3 us
T1bit = (1 + 3 + 2) * Tq = 2us
baudrate = 1/T1bit = 500Kbps
*/
//这里选择波特率
/* CAN_Prescaler bsp
6 1MBps
12 500KBps
24 250KBps
48 125KBps
60 100KBps
120 50KBps
300 20KBps
600 10KBps */
CAN_InitStructure.CAN_Prescaler = 12;
/*Initializes the CAN1 */
CAN_Init(CAN1, &CAN_InitStructure);
/* CAN1 filter init */
/* 16位白名单模式,4个标准ID */
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = ((((uint32_t)0x100<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow = ((((uint32_t)0x101<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ((((uint32_t)0x102<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = ((((uint32_t)0x103<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//映射到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* CAN1 filter init */
/* 32位白名单模式,2个扩展ID */
CAN_FilterInitStructure.CAN_FilterNumber = 1;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = ((((uint32_t)0x18880000<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow = (((uint32_t)0x18880000<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000FFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ((((uint32_t)0x18880001<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (((uint32_t)0x18880001<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000FFFF;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//映射到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* CAN1 filter init */
/* 32位白名单模式,2个扩展ID */
CAN_FilterInitStructure.CAN_FilterNumber = 2;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = ((((uint32_t)0x18880002<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow = (((uint32_t)0x18880002<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000FFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ((((uint32_t)0x18880003<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = (((uint32_t)0x18880003<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0x0000FFFF;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//映射到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
/* IT Configuration for CAN1 */
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);/*!< FIFO 0 message pending Interrupt*/
}
/*
功能:can 发送函数
参数:
ul_id_type: CAN_Id_Standard 或者 CAN_Id_Extended
ul_id_data: 帧ID
*p_data: 数据指针
uc_temp_len: 要发送的数据长度,不大于8
注意:此函数会先后依次判断Mailbox(0、1、2)哪个处于空闲状态,并返回当前发送Mailbox的序号0、1、2
若都不空闲,则返回100
*/
uint8_t sys_can_transmit_message(uint32_t ul_id_type,uint32_t ul_id_data,uint8_t *p_data, uint8_t uc_temp_len)
{
uint8_t i = 0;
uint8_t uc_TransmitMailbox = 100;
CanTxMsg CanTxMsg_structure;
assert_param(IS_CAN_IDTYPE(ul_id_type));
if(uc_temp_len > 8)
uc_temp_len = 8;
if(ul_id_type == CAN_Id_Standard)
{
CanTxMsg_structure.IDE = CAN_ID_STD;
CanTxMsg_structure.StdId = ul_id_data;
}
else
{
CanTxMsg_structure.IDE = CAN_ID_EXT;
CanTxMsg_structure.ExtId = ul_id_data;
}
CanTxMsg_structure.RTR = CAN_RTR_DATA;
CanTxMsg_structure.DLC = uc_temp_len;//data len
for(i = 0;i < uc_temp_len;i++)
{
CanTxMsg_structure.Data[i] = p_data[i];
}
//判断三个Mailbox是否有处于空闲状态的
if(CAN1->TSR&0x1C000000)//等效于 if(CAN1->TSR&(CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)),参考 CAN_Transmit 的函数体定义
{
uc_TransmitMailbox = CAN_Transmit(CAN1, &CanTxMsg_structure);//CAN_Transmit也会判断哪个
}
return uc_TransmitMailbox;
}
int main(void)
{
uint8_t i = 0;
uint8_t j = 0;
uint8_t uc_TransmitMailbox = 100;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
systick_config();
gpio_config();
USART3_config();
can_config();
RUN_LED_OFF;
printf("system power on!!!\r\n");
for(i = 0;i < 8;i++)
uc_temp_table[i] = i;
/* Infinite loop */
while(1)
{
for(j = 0;j < 16;j++)
{
SysTick_Delay_ms(500);
if(j < 8)//前8帧数据为标准帧
uc_TransmitMailbox = sys_can_transmit_message(CAN_Id_Standard,ul_CAN_ID_table[j],uc_temp_table,8);
else//后8帧数据为扩展帧
uc_TransmitMailbox = sys_can_transmit_message(CAN_Id_Extended,ul_CAN_ID_table[j],uc_temp_table,8);
//CAN1 共有3个发送Mailbox(0 1 2),sys_can_transmit_message函数返回的是发送的邮箱号
//当邮箱号为 0 、1、2时,表示发送成功
if(uc_TransmitMailbox < 3)
{
RUN_LED_ON;
SysTick_Delay_ms(50);
RUN_LED_OFF;
}
}
}
}
/* CAN 接收中断处理函数 */
/* 函数内只打印了帧ID,未对数据部分做处理 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg CanRxMsg_Structure;
if(SET == CAN_GetITStatus(CAN1, CAN_IT_FMP0))
{
/* receive data and clear interrupt pending bit */
CAN_Receive(CAN1, CAN_FIFO0, &CanRxMsg_Structure);
if(CanRxMsg_Structure.IDE == CAN_ID_STD)//标准帧
printf("0x%04x\r\n",CanRxMsg_Structure.StdId);
else //扩展帧
printf("0x%08x\r\n",CanRxMsg_Structure.ExtId);
}
}
/* printf函数映射到 USART3 ,根据实际配置映射*/
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
int _sys_exit(int x)
{
x = x;
return 0;
}
int fputc(int ch, FILE *f)
{
USARTx_Send_Single_Byte(USART3,ch);
return ch;
}
/*****END OF FILE****/
串口打印内容
后记
本人水平有限,如有错误,望不吝灵赐教