文章目录前提stm32f103驱动代码stm32f103回环模式stm32f103正常模式我的FreeRTOS补充接收结果前提本文章测试使用的是stm32f103c8t6最小系统板和TJ1050-CAN收发器,使用的是FreeRTOS系统[stm32f103c8t6-freertos基础模板(基础库版)]stm32f103驱动代码MyCan.h#ifndef__MYCAN_H__#define__MYCAN_H__#includestm32f10x.h// Device header/*枚举*/typedefenum{CAN_Filter0_Value0,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{normal0,//正常模式one_Tx_Rx,//回环模式(自发自收)}CAN_Mode_Select;typedefenum{MyCAN_ReceiveFlag_FAIL0,//失败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#includeMyCan.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_PinGPIO_Pin_11;//PA11.上拉输入,因为引脚默认状态是高电平GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU;GPIO_Init(GPIOA,GPIO_InitStructure);//CAN_TXGPIO_InitStructure.GPIO_PinGPIO_Pin_12;//PA12GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP;//用CAN就设置为复用模式推挽输出GPIO_InitStructure.GPIO_SpeedGPIO_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 SS1Tq BS11~16Tq BS21~8Tq SJW1~4Tq (SJW的值仅用于再同步,与波特率的计算无关) */if(model_selectnormal){CAN_InitStruct.CAN_ModeCAN_Mode_Normal;//正常模式}if(model_selectone_Tx_Rx){CAN_InitStruct.CAN_ModeCAN_Mode_LoopBack;//设置CAN外设的测试模式.}/*关于位时序的参数*/CAN_InitStruct.CAN_Prescaler48;//分频系数.相当于(BRP[9:0]1)的值. 波特率36MHZ/48/(123) 125KCAN_InitStruct.CAN_BS1CAN_BS1_2tq;//用于配置BS1段的Tq值.对应公式(TS1[3:0]1)CAN_InitStruct.CAN_BS2CAN_BS2_3tq;//用于配置BS2段的Tq值.对应公式(TS2[2:0]1)CAN_InitStruct.CAN_SJWCAN_SJW_2tq;//再同步补偿宽度.看上面SJW/****************需要这些功能时再启用,目前全是DISABLE*********************//*NART:置1,关闭自动重传, CAN报文只被发送1次, 不管发送的结果如何(成功、出错或仲裁丢失); 置0, 自动重传, CAN硬件在发送报文失败时会一直自动重传直到发送成功 CAN总线默认自动重传*/CAN_InitStruct.CAN_NARTDISABLE;//意思是不开启不自动重传功能/*TXFP:置1,优先级由发送请求的顺序来决定,先请求的先发送; 置0,优先级由报文标识符来决定,标识符值小的先发送(标识符值相等时,邮箱号小的报文先发送)*/CAN_InitStruct.CAN_TXFPDISABLE;//1:先请求先发送,0:ID小的先发送/*RFLM置1接收FIFO锁定FIFO溢出时新收到的报文会被丢弃 置0禁用FIFO锁定FIFO溢出时FIFO中最后收到的报文被新报文覆盖*/CAN_InitStruct.CAN_RFLMDISABLE;//1:FIFO溢出时新报文丢弃;0:FIFO溢出时,最后收到的报文被新报文覆盖/*AWUM: 置1, 自动唤醒, 一旦检测到CAN总线活动, 硬件就自动清零SLEEP,唤醒CAN外设; 置0, 手动唤醒, 软件清零SLEEP, 唤醒CAN外设*/CAN_InitStruct.CAN_AWUMDISABLE;/*TTCM:置1开启时间触发通信功能 置0关闭时间触发通信功能CAN外设内置一个16位的计数器用于记录时间戳 TTCM置1后该计数器在每个CAN位的时间自增一次溢出后归零每个发送邮箱和接收FIFO都有一个TIM[15:0]寄存器发送帧SOF时硬件捕获计数器值到发送 邮箱的TIME寄存器接收帧SOF时硬件捕获计数器值到接收FIFO的TIME寄存器发送邮箱可配置TGT位捕获计数器值的同时也把此值写入到数据帧数据 段的最后两个字节为了使用此功能DLC必须设置为8 */CAN_InitStruct.CAN_TTCMDISABLE;//时间触发通信模式/*ABOM:置1开启离线自动恢复进入离线状态后就自动开启恢复过程 置0关闭离线自动恢复软件必须先请求进入然后再退出初始化模式随后恢复过程才被开启*/CAN_InitStruct.CAN_ABOMDISABLE;//离线自动恢复CAN_Init(CAN1,CAN_InitStruct);/**************4.初始化-过滤器********************///这个滤波器是CAN1和CAN2公用一套CAN_FilterInitTypeDef CAN_FilterInitStruct;/*指定第几个过滤器被初始化,它的范围是0~13;0~13每个过滤器是一样的,所以可以随便指定一个*/CAN_FilterInitStruct.CAN_FilterNumberCAN_Filter0_Value;//赋值为CAN_Filter0_Value(0)/* 俩个32位寄存器需要俩个16位 全通模式:任何报文都可以通过 R1[31:0] 随意 R2[31:0] 0 *//*CAN_FilterIdHigh和CAN_FilterIdLow存入第一组32位ID*/CAN_FilterInitStruct.CAN_FilterIdHigh0x0000;//存入第一组IDCAN_FilterInitStruct.CAN_FilterIdLow0x0000;//存入第二组ID/*CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow存入第二组32位ID*/CAN_FilterInitStruct.CAN_FilterMaskIdHigh0x0000;//存入第一组ID的屏蔽位CAN_FilterInitStruct.CAN_FilterMaskIdLow0x0000;//存入第二组ID的屏蔽位/*选择过滤器位宽*/CAN_FilterInitStruct.CAN_FilterScaleCAN_FilterScale_32bit;/*选择过滤器模式*/CAN_FilterInitStruct.CAN_FilterModeCAN_FilterMode_IdMask;/*关联寄存器,关联设置*/CAN_FilterInitStruct.CAN_FilterFIFOAssignmentCAN_Filter_FIFO0;/*激活寄存器,激活设置*/CAN_FilterInitStruct.CAN_FilterActivationENABLE;//相当于过滤器开关CAN_FilterInit(CAN_FilterInitStruct);}/** 发送报文函数(标准数据帧) CAN_Transmit再封装 参数: ID Len 传入数据长度 Data 传入数据内容 */voidMyCAN_Transmit_standard(uint32_tID,uint8_tLen,uint8_t*Data){CanTxMsg TxMessage;TxMessage.StdIdID;//标准IDTxMessage.ExtIdID;//扩展IDTxMessage.IDECAN_Id_Standard;//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效TxMessage.RTRCAN_RTR_Data;//遥控标志位TxMessage.DLCLen;//数据段长度:0~8for(uint8_ti0;iLen;i){TxMessage.Data[i]Data[i];//数据段内容}uint8_tTransmitMailboxCAN_Transmit(CAN1,TxMessage);/*超时退出*/uint32_tTimeOut0;/*等待发送完成*/while(CAN_TransmitStatus(CAN1,TransmitMailbox)!CAN_TxStatus_Ok){TimeOut;if(TimeOut100000){break;}}}/** 发送报文函数(扩展数据帧) CAN_Transmit再封装 参数: ID Len 传入数据长度 Data 传入数据内容 */voidMyCAN_Transmit_extended(uint32_tID,uint8_tLen,uint8_t*Data){CanTxMsg TxMessage;TxMessage.StdIdID;//标准IDTxMessage.ExtIdID;//扩展ID//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效TxMessage.IDECAN_Id_Extended;//CAN_ID_STDTxMessage.RTRCAN_RTR_Data;//遥控标志位TxMessage.DLCLen;//数据段长度:0~8for(uint8_ti0;iLen;i){TxMessage.Data[i]Data[i];//数据段内容}uint8_tTransmitMailboxCAN_Transmit(CAN1,TxMessage);/*超时退出*/uint32_tTimeOut0;/*等待发送完成*/while(CAN_TransmitStatus(CAN1,TransmitMailbox)!CAN_TxStatus_Ok){TimeOut;if(TimeOut100000){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.IDECAN_Id_Standard){*IDRxMessage.StdId;}else{*IDRxMessage.ExtId;}// 3. 解析数据长度和内容仅数据帧if(RxMessage.RTRCAN_RTR_DATA){*LenRxMessage.DLC;for(uint8_ti0;i*Len;i){Data[i]RxMessage.Data[i];}}else{*Len0;}}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_ti0;i100000;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__#includestm32f10x.h#includeFreeRTOS.h#includetask.h#includequeue.h#includemytask.h#endif//main.c#includemain.hQueueHandle_t xQueue;intmain(void){xQueuexQueueCreate(5,sizeof(int));// 创建队列容量5数据类型intxTaskCreate(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__#includestm32f10x.h#includeFreeRTOS.h#includetask.h#includequeue.h/***************include*********************/#includeMyCan.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#includemytask.hvoidDelay(u32 count){u32 i0,j;for(;icount;i)for(j0;j1000;j);}voidLED_Task(void*param){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_PinGPIO_Pin_13;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_SpeedGPIO_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_ti0;i100000;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自己填充的)