文章目录
- 前提
- stm32f103驱动代码
- stm32f103回环模式
- stm32f103正常模式
- 我的FreeRTOS补充
- 接收结果
前提
本文章测试使用的是
stm32f103c8t6最小系统板和TJ1050-CAN收发器,使用的是FreeRTOS系统[stm32f103c8t6-freertos基础模板(基础库版)]
stm32f103驱动代码
MyCan.h
#ifndef__MYCAN_H__#define__MYCAN_H__#include"stm32f10x.h"// Device header/*枚举*/typedefenum{CAN_Filter0_Value=0,CAN_Filter1_Value,CAN_Filter2_Value,CAN_Filter3_Value,CAN_Filter4_Value,CAN_Filter5_Value,CAN_Filter6_Value,CAN_Filter7_Value,CAN_Filter8_Value,CAN_Filter9_Value,CAN_Filter10_Value,CAN_Filter11_Value,CAN_Filter12_Value,CAN_Filter13_Value,}CAN_Filter_Value;//过滤器选择/*模式切换*/typedefenum{normal=0,//正常模式one_Tx_Rx,//回环模式(自发自收)}CAN_Mode_Select;typedefenum{MyCAN_ReceiveFlag_FAIL=0,//失败MyCAN_ReceiveFlag_OK//成功}MyCAN_ReceiveFlag_VALUE;//FIFO状态/**********function***********/voidMyCAN_Init(uint8_tmodel_select);voidMyCAN_Transmit_standard(uint32_tID,uint8_tLen,uint8_t*Data);//发送报文-标准数据帧voidMyCAN_Transmit_extended(uint32_tID,uint8_tLen,uint8_t*Data);//发送报文-扩展数据帧uint8_tMyCAN_ReceiveFlag(void);//读取FIFO中是否有报文voidMyCAN_Receive(uint32_t*ID,uint8_t*Len,uint8_t*Data);//Can总线接收-查询接收#endifMyCan.c
#include"MyCan.h"/** Can模式使能 model_select:正常模式还是回环(自发自收)模式 */voidMyCAN_Init(uint8_tmodel_select){/****************1.初始化时钟******************/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//1.开启GPIOA时钟//CAN默认复用的是PA11(CAN_RX)和PA12(CAN_TX),单片机的CAN_TX和CAN_RX不需要想串口一样交叉相连,RX->CAN RX;TX-> CAN TX即可//可以映射到PB8(CAN_RX)和PB9(CAN_TX)//开启CAN外设的时钟,CAN外设挂载在APB1的RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);/************2.初始化GPIO***********************/GPIO_InitTypeDef GPIO_InitStructure;//CAN_RXGPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;//PA11.上拉输入,因为引脚默认状态是高电平GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_Init(GPIOA,&GPIO_InitStructure);//CAN_TXGPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;//PA12GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//用CAN就设置为复用模式推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/**************3.初始化CAN********************/CAN_InitTypeDef CAN_InitStruct;/* 波特率=APB1时钟频率/分频系数/一位的Tq数量 =36MHZ/((BRP[9:0]+1)/(1+(TS1[3:0]+1)+(TS2[2:0]+1)) > 高速CAN的波特率范围是125K~1M > SS=1Tq > BS1=1~16Tq > BS2=1~8Tq > SJW=1~4Tq (SJW的值仅用于再同步,与波特率的计算无关) */if(model_select==normal){CAN_InitStruct.CAN_Mode=CAN_Mode_Normal;//正常模式}if(model_select==one_Tx_Rx){CAN_InitStruct.CAN_Mode=CAN_Mode_LoopBack;//设置CAN外设的测试模式.}/*关于位时序的参数*/CAN_InitStruct.CAN_Prescaler=48;//分频系数.相当于(BRP[9:0]+1)的值. 波特率=36MHZ/48/(1+2+3) = 125KCAN_InitStruct.CAN_BS1=CAN_BS1_2tq;//用于配置BS1段的Tq值.对应公式(TS1[3:0]+1)CAN_InitStruct.CAN_BS2=CAN_BS2_3tq;//用于配置BS2段的Tq值.对应公式(TS2[2:0]+1)CAN_InitStruct.CAN_SJW=CAN_SJW_2tq;//再同步补偿宽度.看上面SJW/****************需要这些功能时再启用,目前全是DISABLE*********************//*NART:置1,关闭自动重传, CAN报文只被发送1次, 不管发送的结果如何(成功、出错或仲裁丢失); 置0, 自动重传, CAN硬件在发送报文失败时会一直自动重传直到发送成功 CAN总线默认自动重传*/CAN_InitStruct.CAN_NART=DISABLE;//意思是不开启不自动重传功能/*TXFP:置1,优先级由发送请求的顺序来决定,先请求的先发送; 置0,优先级由报文标识符来决定,标识符值小的先发送(标识符值相等时,邮箱号小的报文先发送)*/CAN_InitStruct.CAN_TXFP=DISABLE;//1:先请求先发送,0:ID小的先发送/*RFLM:置1,接收FIFO锁定,FIFO溢出时,新收到的报文会被丢弃; 置0,禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖*/CAN_InitStruct.CAN_RFLM=DISABLE;//1:FIFO溢出时新报文丢弃;0:FIFO溢出时,最后收到的报文被新报文覆盖/*AWUM: 置1, 自动唤醒, 一旦检测到CAN总线活动, 硬件就自动清零SLEEP,唤醒CAN外设; 置0, 手动唤醒, 软件清零SLEEP, 唤醒CAN外设*/CAN_InitStruct.CAN_AWUM=DISABLE;/*TTCM:置1,开启时间触发通信功能; 置0,关闭时间触发通信功能CAN外设内置一个16位的计数器,用于记录时间戳 TTCM置1后,该计数器在每个CAN位的时间自增一次,溢出后归零每个发送邮箱和接收FIFO都有一个TIM[15:0]寄存器,发送帧SOF时,硬件捕获计数器值到发送 邮箱的TIME寄存器,接收帧SOF时,硬件捕获计数器值到接收FIFO的TIME寄存器发送邮箱可配置TGT位,捕获计数器值的同时,也把此值写入到数据帧数据 段的最后两个字节,为了使用此功能,DLC必须设置为8 */CAN_InitStruct.CAN_TTCM=DISABLE;//时间触发通信模式/*ABOM:置1,开启离线自动恢复,进入离线状态后,就自动开启恢复过程; 置0,关闭离线自动恢复,软件必须先请求进入然后再退出初始化模式,随后恢复过程才被开启*/CAN_InitStruct.CAN_ABOM=DISABLE;//离线自动恢复CAN_Init(CAN1,&CAN_InitStruct);/**************4.初始化-过滤器********************///这个滤波器是CAN1和CAN2公用一套CAN_FilterInitTypeDef CAN_FilterInitStruct;/*指定第几个过滤器被初始化,它的范围是0~13;0~13每个过滤器是一样的,所以可以随便指定一个*/CAN_FilterInitStruct.CAN_FilterNumber=CAN_Filter0_Value;//赋值为CAN_Filter0_Value(0)/* 俩个32位寄存器需要俩个16位 全通模式:任何报文都可以通过 R1[31:0] = 随意 R2[31:0] = 0 *//*CAN_FilterIdHigh和CAN_FilterIdLow存入第一组32位ID*/CAN_FilterInitStruct.CAN_FilterIdHigh=0x0000;//存入第一组IDCAN_FilterInitStruct.CAN_FilterIdLow=0x0000;//存入第二组ID/*CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow存入第二组32位ID*/CAN_FilterInitStruct.CAN_FilterMaskIdHigh=0x0000;//存入第一组ID的屏蔽位CAN_FilterInitStruct.CAN_FilterMaskIdLow=0x0000;//存入第二组ID的屏蔽位/*选择过滤器位宽*/CAN_FilterInitStruct.CAN_FilterScale=CAN_FilterScale_32bit;/*选择过滤器模式*/CAN_FilterInitStruct.CAN_FilterMode=CAN_FilterMode_IdMask;/*关联寄存器,关联设置*/CAN_FilterInitStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;/*激活寄存器,激活设置*/CAN_FilterInitStruct.CAN_FilterActivation=ENABLE;//相当于过滤器开关CAN_FilterInit(&CAN_FilterInitStruct);}/** 发送报文函数(标准数据帧) CAN_Transmit再封装 参数: ID Len 传入数据长度 Data 传入数据内容 */voidMyCAN_Transmit_standard(uint32_tID,uint8_tLen,uint8_t*Data){CanTxMsg TxMessage;TxMessage.StdId=ID;//标准IDTxMessage.ExtId=ID;//扩展IDTxMessage.IDE=CAN_Id_Standard;//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效TxMessage.RTR=CAN_RTR_Data;//遥控标志位TxMessage.DLC=Len;//数据段长度:0~8for(uint8_ti=0;i<Len;i++){TxMessage.Data[i]=Data[i];//数据段内容}uint8_tTransmitMailbox=CAN_Transmit(CAN1,&TxMessage);/*超时退出*/uint32_tTimeOut=0;/*等待发送完成*/while(CAN_TransmitStatus(CAN1,TransmitMailbox)!=CAN_TxStatus_Ok){TimeOut++;if(TimeOut>100000){break;}}}/** 发送报文函数(扩展数据帧) CAN_Transmit再封装 参数: ID Len 传入数据长度 Data 传入数据内容 */voidMyCAN_Transmit_extended(uint32_tID,uint8_tLen,uint8_t*Data){CanTxMsg TxMessage;TxMessage.StdId=ID;//标准IDTxMessage.ExtId=ID;//扩展ID//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效TxMessage.IDE=CAN_Id_Extended;//CAN_ID_STDTxMessage.RTR=CAN_RTR_Data;//遥控标志位TxMessage.DLC=Len;//数据段长度:0~8for(uint8_ti=0;i<Len;i++){TxMessage.Data[i]=Data[i];//数据段内容}uint8_tTransmitMailbox=CAN_Transmit(CAN1,&TxMessage);/*超时退出*/uint32_tTimeOut=0;/*等待发送完成*/while(CAN_TransmitStatus(CAN1,TransmitMailbox)!=CAN_TxStatus_Ok){TimeOut++;if(TimeOut>100000){break;}}}/** @ MyCAN_ReceiveFlag,读取FIFO中是否有报文 1.判断接收FIFO里是否有报文 2.读取接收FIFO,把报文内容读出来 返回值: 有报文返回1(MyCAN_ReceiveFlag_OK),没有返回0(MyCAN_ReceiveFlag_FAIL) */uint8_tMyCAN_ReceiveFlag(void){if(CAN_MessagePending(CAN1,CAN_FIFO0)>0){returnMyCAN_ReceiveFlag_OK;//有报文}returnMyCAN_ReceiveFlag_FAIL;//没有报文}voidMyCAN_Receive(uint32_t*ID,uint8_t*Len,uint8_t*Data){CanRxMsg RxMessage;// 1. 从 FIFO0 读取一帧报文CAN_Receive(CAN1,CAN_FIFO0,&RxMessage);// 2. 解析 IDif(RxMessage.IDE==CAN_Id_Standard){*ID=RxMessage.StdId;}else{*ID=RxMessage.ExtId;}// 3. 解析数据长度和内容(仅数据帧)if(RxMessage.RTR==CAN_RTR_DATA){*Len=RxMessage.DLC;for(uint8_ti=0;i<*Len;i++){Data[i]=RxMessage.Data[i];}}else{*Len=0;}}stm32f103回环模式
白话就是stm32内置的CAN发送给CAN收发器后,通过CAN收发器自发自收
通过驱动MyCcan.c和MyCan.h来写
MyCAN_Init(one_Tx_Rx);//自发自收发送
uint8_tCan_Tx_Data1[]={0x66,0x88,0x99};MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);//MyCAN_Transmit_standard(id,发送数组长度,数组)// 等待一小段时间(回环模式下发送后几乎立即收到)for(uint32_ti=0;i<100000;i++);接收
// 检查接收 FIFOif(CAN_MessagePending(CAN1,CAN_FIFO0)>0){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];MyCAN_Receive(&RxID,&RxLen,RxData);//在这下面一行断点可以添加循环发送(我这里用来
FreeRTOS最小系统所以延时是vTaskDelay())
while(1){MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);// 等待一小段时间(回环模式下发送后几乎立即收到)vTaskDelay(200);// 检查接收 FIFOif(CAN_MessagePending(CAN1,CAN_FIFO0)>0){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];MyCAN_Receive(&RxID,&RxLen,RxData);// 使用上面正确的接收函数vTaskDelay(10);CAN1->RF0R|=CAN_RF0R_RFOM0;// 对RFOM0位置1,释放一个报文vTaskDelay(200);}}stm32f103正常模式
这里就需要俩块stm32f103c8t6最小系统板和俩块CAN收发器了
通过驱动MyCcan.c和MyCan.h来写
MyCAN_Init(normal);//正常模式一块stm32板和CAN收发板作为
发送
while(1){uint8_tCan_Tx_Data1[]={0x66,0x88,0x99};MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);vTaskDelay(500);//500ms}一块stm32板和CAN收发板作为
接收
while(1){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];if(MyCAN_ReceiveFlag()==MyCAN_ReceiveFlag_OK){MyCAN_Receive(&RxID,&RxLen,RxData);}vTaskDelay(500);// 轮询间隔}我的FreeRTOS补充
此代码停留在CAN正常模式的接收
//main.h#ifndef__MAIN_H__#define__MAIN_H__#include"stm32f10x.h"#include"FreeRTOS.h"#include"task.h"#include"queue.h"#include"mytask.h"#endif//main.c#include"main.h"QueueHandle_t xQueue;intmain(void){xQueue=xQueueCreate(5,sizeof(int));// 创建队列(容量5,数据类型int)xTaskCreate(LED_Task,"LED Task",128,NULL,2,NULL);xTaskCreate(CAN1_Task,"CAN1 Task",128,NULL,2,NULL);vTaskStartScheduler();// 启动调度器while(1);// 调度器启动后不会返回}//mytask.h#ifndef__MYTASK_H__#define__MYTASK_H__#include"stm32f10x.h"#include"FreeRTOS.h"#include"task.h"#include"queue.h"/***************include*********************/#include"MyCan.h"#defineone_Tx_Rx_status0//回环模式设置;0:关闭,1开启.不能与normal_status同时使用#definenormal_status1//正常模式设置;0:关闭,1开启.不能与one_Tx_Rx同时使用#definenormal_tx0//正常模式发送设置;0:关闭,1开启.不能与normal_rx同时使用#definenormal_rx1//正常模式接收设置;0:关闭,1开启.不能与normal_tx同时使用voidLED_Task(void*param);voidCAN1_Task(void*param);voidDelay(u32 count);#endif//mytask.c#include"mytask.h"voidDelay(u32 count){u32 i=0,j;for(;i<count;i++)for(j=0;j<1000;j++);}voidLED_Task(void*param){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度为50MHzGPIO_Init(GPIOC,&GPIO_InitStructure);//初始化GPIOC.13GPIO_SetBits(GPIOC,GPIO_Pin_13);//PC.13 输出高while(1){GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay(5000);GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay(5000);}}voidCAN1_Task(void*param){#ifone_Tx_Rx_status/*回环模式*/MyCAN_Init(one_Tx_Rx);uint8_tCan_Tx_Data1[]={0x66,0x88,0x99};MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);// 等待一小段时间(回环模式下发送后几乎立即收到)for(uint32_ti=0;i<100000;i++);// 检查接收 FIFOif(CAN_MessagePending(CAN1,CAN_FIFO0)>0){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];MyCAN_Receive(&RxID,&RxLen,RxData);// 使用上面正确的接收函数// 在此处设置断点,观察 rxId 和 rxData// 期望 rxId = 0x123, rxData[0]=0x66, rxData[1]=0x88}while(1){MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);// 等待一小段时间(回环模式下发送后几乎立即收到)vTaskDelay(200);// 检查接收 FIFOif(CAN_MessagePending(CAN1,CAN_FIFO0)>0){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];MyCAN_Receive(&RxID,&RxLen,RxData);// 使用上面正确的接收函数vTaskDelay(10);CAN1->RF0R|=CAN_RF0R_RFOM0;// 对RFOM0位置1,释放一个报文vTaskDelay(200);// 在此处设置断点,观察 rxId 和 rxData// 期望 rxId = 0x123, rxData[0]=0x66, rxData[1]=0x88}}#endif#ifnormal_status/*正常模式*/MyCAN_Init(normal);#ifnormal_tx/*正常模式-发送*/while(1){uint8_tCan_Tx_Data1[]={0x66,0x88,0x99};MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);vTaskDelay(500);//500ms}#endif#ifnormal_rx/*正常模式接收*/while(1){uint32_tRxID;uint8_tRxLen;uint8_tRxData[8];if(MyCAN_ReceiveFlag()==MyCAN_ReceiveFlag_OK){MyCAN_Receive(&RxID,&RxLen,RxData);}vTaskDelay(500);// 轮询间隔}#endif#endif}接收结果
我用Debug来查看
上述发送的确实是
- ID: 0x123
- 长度: 3
- 数据: 0x66,0x88,0x99
(后续是数组是能存储8位,剩下的0xA9是CAN自己填充的)