CAN总线
2018-09-02 11:03
148 查看
一、编程要点
1、初始化相关外设时钟及GPIO
2、CAN模式初始化
3、筛选器配置
4、配置接收中断和写中断服务函数
5、CAN收发函数
二、CAN基础知识
1、物理层
采用差分信号,理论上无节点限制
2、协议层
一个完整的数据位由 8~25 个 Tq 组成,高低电平直接代表信号逻辑 0(显性) 或逻辑 1(隐性)信号的采样点位于 PBS1 段与 PBS2 段之间,通过控制各段的长度,可以对采样点的位置进行偏移,以便准确地采样
3、CAN的报文及结构
4、STM32 CAN外设
1. 四种工作模式(正常、静默、回环、静默回环)
2. 波特率设置方法
3. 发送及接收邮箱
3个发送邮箱,2个接收一共6个接收邮箱
4. 验收筛选器
4种工作模式
工作在掩码模式时
三、结构体&库函数
1、CAN初始化结构体
typedef struct { uint16_t CAN_Prescaler; /*!<CAN时钟预分频 1M为4 */ uint8_t CAN_Mode; /*!< 四种工作模式 */ uint8_t CAN_SJW; /*!< SWJ的极限值 用于再同步 */ uint8_t CAN_BS1; /*!< 见上图 */ uint8_t CAN_BS2; /*!< */ FunctionalState CAN_TTCM; /*!< 是否使能时间触发功能 */ FunctionalState CAN_ABOM; /*!< 自动离线 */ FunctionalState CAN_AWUM; /*!< 自动唤醒 */ FunctionalState CAN_NART; /*!< 自动重传*/ FunctionalState CAN_RFLM; /*!< 是否使能锁定FIFO 不使能按先后顺序 */ FunctionalState CAN_TXFP; /*!< 报文优先级判定enable(先后顺序)disable(ID优先级) */ } CAN_InitTypeDef;
2、过滤器结构体
typedef struct { uint16_t CAN_FilterIdHigh; /*!< CAN_FxR1 */ uint16_t CAN_FilterIdLow; /*!< CAN_FxR1 */ uint16_t CAN_FilterMaskIdHigh; /*!< CAN_FxR1 */ uint16_t CAN_FilterMaskIdLow; /*!< CAN_FxR1 见上图 */ uint16_t CAN_FilterFIFOAssignment; /*!< 选择报文储存在哪个FOFI */ uint8_t CAN_FilterNumber; /*!< 筛选器编号 0—13 */ uint8_t CAN_FilterMode; /*!< CAN筛选器工作模式 */ uint8_t CAN_FilterScale; /*!< 配置筛选器尺度 16/32位 */ FunctionalState CAN_FilterActivation; /*!< 是否使能该筛选器 */ } CAN_FilterInitTypeDef;
3、中断配置及服务函数
void NVIC_Configuration(void) //配置中断向量控制器 { NVIC_InitTypeDef NVIC_InitStructure; // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //选择中断控制器组 NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //选择中断源 NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE; //使能中段 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;//抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级 NVIC_Init(&NVIC_InitStructure); //初始化结构体将值写入 } void USB_LP_CAN1_RX0_IRQHandler(void) { CAN_Receive(CAN1,CAN_FIFO0, &CanRxData); flag=1; }
4、接收发送结构体
typedef struct { uint32_t StdId; /*!< 标准ID 0 to 0x7FF. */ uint32_t ExtId; /*!< 扩展ID 0 to 0x1FFFFFFF. */ uint8_t IDE; /*!< 扩展标志 标准CAN_Id_Standard 扩展CAN_Id_Extended */ uint8_t RTR; /*!< 远程帧标志 数据CAN_RTR_Data 远程CAN_RTR_Remote */ uint8_t DLC; /*!< 报文数据帧长度 0 to 8 */ uint8_t Data[8]; /*!< 报文数据内容 */ uint8_t FMI; /*接收特有 存储了 本报文是由经过筛选器存储进 FIFO 的, 0-0xFF */ 26 } CanRxMsg;
5、数据发送及接收函数
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage)
box = CAN_Transmit(CAN1,&CanTxData) @brief :CAN数据发送 @param1:CAN号 @param2:要发送的数据地址 &CanTxData @retval:发送邮箱号
CAN_Receive(CAN1,CAN_FIFO0, &CanRxData); @brief :CAN数据接收 @param1:CAN号 @param2:接收FIFO号 @param3:接收的数据储存地址
6、消息传输状态检查函数
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox) while(CAN_TransmitStatus(CAN1,box) == CAN_TxStatus_Failed); @brief :传输状态检查 @param1:CAN号 @param2:发送邮箱号 @retval:发送结果
四、其他注意事项
1、CAN引脚模式
TX GPIO_Mode_AF_PP
RX GPIO_Mode_IPU/GPIO_Mode_IN_FLOATING
2、引脚复用功能开启
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE)
五、程序源码
1、bsp_can.h
#ifndef __bsp_can_H #define __bsp_can_H #include "stm32f10x.h" #define CAN_CLK RCC_APB1Periph_CAN1 #define CAN_RX_GPIO_CLK (RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO) #define CAN_RX_GPIO_PORT GPIOB #define CAN_RX_GPIO_PIN GPIO_Pin_8 #define CAN_TX_GPIO_CLK RCC_APB2Periph_GPIOB #define CAN_TX_GPIO_PORT GPIOB #define CAN_TX_GPIO_PIN GPIO_Pin_9 #define PASS_ID ((uint32_t)0X1314) void CAN_config(void); #endif //__bsp_can_H
2、bsp_can.c
#include "bsp_can.h" extern CanTxMsg CanTxData; extern CanRxMsg CanRxData; void CAN_GPIO_config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(CAN_RX_GPIO_CLK,ENABLE); RCC_APB2PeriphClockCmd(CAN_TX_GPIO_CLK,ENABLE); //相关时钟 GPIO_InitStructure.GPIO_Pin = CAN_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入/浮空输入 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = CAN_TX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure); } void CAN_Init_config(void) { CAN_InitTypeDef CAN_InitStructure; RCC_APB1PeriphClockCmd (RCC_APB1Periph_CAN1 , ENABLE ); //can时钟 CAN_InitStructure.CAN_Prescaler = 4; //can时钟分频 CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; //工作模式 CAN_InitStructure.CAN_BS1 = CAN_BS1_5tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; CAN_InitStructure.CAN_SJW = CAN_SJW_2tq; CAN_InitStructure.CAN_TTCM = DISABLE; //时间触发 CAN_InitStructure.CAN_ABOM = ENABLE; //自动离线 CAN_InitStructure.CAN_AWUM = ENABLE; //自动唤醒 CAN_InitStructure.CAN_NART = ENABLE; //自动重传 CAN_InitStructure.CAN_TXFP = DISABLE; //报文优先级判定enable(先后顺序)disable(ID优先级) CAN_InitStructure.CAN_RFLM = ENABLE; //是否使能锁定FIFO 不使能按先后顺序 CAN_Init(CAN1,&CAN_InitStructure); } void CAN_CAN_FilterInit(void) { CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //筛选器模式 CAN_FilterInitStructure.CAN_FilterNumber = 0; //筛选器组号 0-13 CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; //存储FIFO CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //配置筛选器尺度 16/32位 CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能该筛选器 CAN_FilterInitStructure.CAN_FilterIdHigh = ((PASS_ID<<3 |CAN_Id_Extended |CAN_RTR_Data)&0xFFFF0000)>>16; CAN_FilterInitStructure.CAN_FilterIdLow = ((PASS_ID<<3 |CAN_Id_Extended |CAN_RTR_Data)&0xFFFF); CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0XFFFF; CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0XFFFF; CAN_FilterInit(&CAN_FilterInitStructure); CAN_ITConfig (CAN1,CAN_IT_FMP0,ENABLE); //使能CAN接收中断 } void NVIC_Configuration(void) //配置中断向量控制器 { NVIC_InitTypeDef NVIC_InitStructure; // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //选择中断控制器组 NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn; //选择中断源 NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE; //使能中段 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority =1; //子优先级 NVIC_Init(&NVIC_InitStructure); //初始化结构体将值写入 } void CAN_config(void) { CAN_GPIO_config(); //GPIO配置 CAN_Init_config(); //初始化结构体 CAN_CAN_FilterInit(); //筛选器配置 NVIC_Configuration(); //中断 }
3.main.c
#include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h> #include "bsp_led.h" #include "usart.h" #include "bsp_can.h" #include "bsp_key.h" #define GPIOB_ODR_Addr (GPIOB_BASE+0X0C) #define PBout(n) *(unsigned int *)((GPIOB_ODR_Addr & 0xF0000000)+0x02000000+((GPIOB_ODR_Addr&0x00FFFFFF)<<5)+(n<<2)) extern CanTxMsg CanTxData; extern CanRxMsg CanRxData; extern u8 flag; void delay(uint32_t i) { while(i--); } int main(void) { uint8_t box=5; flag=0; LED_GPIO_Config(); KEY_GPIO_Config(); USART_config(); CAN_config(); PBout(0)=1; printf("\r\n 串口初始化完成\r\n"); while(1) { if(key_san(GPIOA, GPIO_Pin_0)) { CanTxData.StdId = 0; //标准ID CanTxData.ExtId = PASS_ID; //扩展ID CanTxData.IDE = CAN_Id_Extended; //扩展标志 标准CAN_Id_Standard 扩展CAN_Id_Extended CanTxData.DLC = 1; //数据位长度 CanTxData.Data[0] = 10; //数据内容 CanTxData.RTR = CAN_RTR_Data; //远程帧标志 数据CAN_RTR_Data 远程CAN_RTR_Remote box = CAN_Transmit(CAN1,&CanTxData); while(CAN_TransmitStatus(CAN1,box) == CAN_TxStatus_Failed); //检测报文是否发送完成 //printf("\r\n 发送结果:%d\r\n",CAN_TransmitStatus(CAN1,box)); printf("\r\n 数据包发送完成\r\n"); PBout(0)=0; } if(flag==1) { printf("\r\n接收到的数据:%d\r\n",CanRxData.Data[0]); flag = 0; } } }阅读更多