文章目录
-
-
- 前提
- stm32f103驱动代码
- stm32f103回环模式
- stm32f103正常模式
- 我的FreeRTOS补充
-
前提
本文章测试使用的是
stm32f103c8t6最小系统板和TJ1050-CAN收发器,使用的是FreeRTOS系统stm32f103c8t6-freertos基础模板(基础库版)
stm32f103驱动代码
MyCan.h
c
#ifndef __MYCAN_H__
#define __MYCAN_H__
#include "stm32f10x.h" // Device header
/*枚举*/
typedef enum {
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;//过滤器选择
/*模式切换*/
typedef enum{
normal = 0,//正常模式
one_Tx_Rx, //回环模式(自发自收)
}CAN_Mode_Select;
typedef enum{
MyCAN_ReceiveFlag_FAIL = 0, //失败
MyCAN_ReceiveFlag_OK //成功
}MyCAN_ReceiveFlag_VALUE;//FIFO状态
/**********function***********/
void MyCAN_Init(uint8_t model_select);
void MyCAN_Transmit_standard(uint32_t ID,uint8_t Len,uint8_t *Data);//发送报文-标准数据帧
void MyCAN_Transmit_extended(uint32_t ID,uint8_t Len,uint8_t *Data);//发送报文-扩展数据帧
uint8_t MyCAN_ReceiveFlag(void);//读取FIFO中是否有报文
void MyCAN_Receive(uint32_t *ID,uint8_t *Len,uint8_t *Data);//Can总线接收-查询接收
#endif
MyCan.c
c
#include "MyCan.h"
/**
Can模式使能
model_select:正常模式还是回环(自发自收)模式
*/
void MyCAN_Init(uint8_t model_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_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PA11.上拉输入,因为引脚默认状态是高电平
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//CAN_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PA12
GPIO_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) = 125K
CAN_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;//存入第一组ID
CAN_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 传入数据内容
*/
void MyCAN_Transmit_standard(uint32_t ID,uint8_t Len,uint8_t *Data){
CanTxMsg TxMessage;
TxMessage.StdId = ID;//标准ID
TxMessage.ExtId = ID;//扩展ID
TxMessage.IDE = CAN_Id_Standard;//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效
TxMessage.RTR = CAN_RTR_Data;//遥控标志位
TxMessage.DLC = Len;//数据段长度:0~8
for(uint8_t i=0;i<Len;i++){
TxMessage.Data[i] = Data[i];//数据段内容
}
uint8_t TransmitMailbox= CAN_Transmit(CAN1,&TxMessage);
/*超时退出*/
uint32_t TimeOut = 0;
/*等待发送完成*/
while(CAN_TransmitStatus(CAN1,TransmitMailbox) != CAN_TxStatus_Ok){
TimeOut++;
if(TimeOut > 100000){
break;
}
}
}
/**
发送报文函数(扩展数据帧)
CAN_Transmit再封装
参数:
ID
Len 传入数据长度
Data 传入数据内容
*/
void MyCAN_Transmit_extended(uint32_t ID,uint8_t Len,uint8_t *Data){
CanTxMsg TxMessage;
TxMessage.StdId = ID;//标准ID
TxMessage.ExtId = ID;//扩展ID
//扩展标志位.标准格式StdId有效,ExtId无效;扩展格式StdId无效,ExtId有效
TxMessage.IDE = CAN_Id_Extended;//CAN_ID_STD
TxMessage.RTR = CAN_RTR_Data;//遥控标志位
TxMessage.DLC = Len;//数据段长度:0~8
for(uint8_t i=0;i<Len;i++){
TxMessage.Data[i] = Data[i];//数据段内容
}
uint8_t TransmitMailbox= CAN_Transmit(CAN1,&TxMessage);
/*超时退出*/
uint32_t TimeOut = 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_t MyCAN_ReceiveFlag(void){
if(CAN_MessagePending(CAN1,CAN_FIFO0) > 0){
return MyCAN_ReceiveFlag_OK;//有报文
}
return MyCAN_ReceiveFlag_FAIL;//没有报文
}
void MyCAN_Receive(uint32_t *ID, uint8_t *Len, uint8_t *Data){
CanRxMsg RxMessage;
// 1. 从 FIFO0 读取一帧报文
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
// 2. 解析 ID
if(RxMessage.IDE == CAN_Id_Standard){
*ID = RxMessage.StdId;
} else {
*ID = RxMessage.ExtId;
}
// 3. 解析数据长度和内容(仅数据帧)
if(RxMessage.RTR == CAN_RTR_DATA){
*Len = RxMessage.DLC;
for(uint8_t i = 0; i < *Len; i++){
Data[i] = RxMessage.Data[i];
}
} else {
*Len = 0;
}
}
stm32f103回环模式
白话就是stm32内置的CAN发送给CAN收发器后,通过CAN收发器自发自收
c
MyCAN_Init(one_Tx_Rx);//自发自收
发送
c
uint8_t Can_Tx_Data1[]={0x66,0x88,0x99};
MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);//MyCAN_Transmit_standard(id,发送数组长度,数组)
// 等待一小段时间(回环模式下发送后几乎立即收到)
for(uint32_t i = 0; i < 100000; i++);
接收
c
// 检查接收 FIFO
if(CAN_MessagePending(CAN1, CAN_FIFO0) > 0){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[8];
MyCAN_Receive(&RxID, &RxLen, RxData); //在这下面一行断点
可以添加循环发送(我这里用来
FreeRTOS最小系统所以延时是vTaskDelay())
c
while(1){
MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);
// 等待一小段时间(回环模式下发送后几乎立即收到)
vTaskDelay(200);
// 检查接收 FIFO
if(CAN_MessagePending(CAN1, CAN_FIFO0) > 0){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[8];
MyCAN_Receive(&RxID, &RxLen, RxData); // 使用上面正确的接收函数
vTaskDelay(10);
CAN1->RF0R |= CAN_RF0R_RFOM0; // 对RFOM0位置1,释放一个报文
vTaskDelay(200);
}
}
stm32f103正常模式
这里就需要俩块stm32f103c8t6最小系统板和俩块CAN收发器了
通过驱动MyCcan.c和MyCan.h来写
c
MyCAN_Init(normal);//正常模式
一块stm32板和CAN收发板作为
发送
c
while(1){
uint8_t Can_Tx_Data1[]={0x66,0x88,0x99};
MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);
vTaskDelay(500);//500ms
}
一块stm32板和CAN收发板作为
接收
c
while(1){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[8];
if (MyCAN_ReceiveFlag() == MyCAN_ReceiveFlag_OK)
{
MyCAN_Receive(&RxID, &RxLen, RxData);
}
vTaskDelay(500); // 轮询间隔
}
我的FreeRTOS补充
此代码停留在CAN正常模式的接收
c
//main.h
#ifndef __MAIN_H__
#define __MAIN_H__
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "mytask.h"
#endif
c
//main.c
#include "main.h"
QueueHandle_t xQueue;
int main(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); // 调度器启动后不会返回
}
c
//mytask.h
#ifndef __MYTASK_H__
#define __MYTASK_H__
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/***************include*********************/
#include "MyCan.h"
#define one_Tx_Rx_status 0//回环模式设置;0:关闭,1开启.不能与normal_status同时使用
#define normal_status 1 //正常模式设置;0:关闭,1开启.不能与one_Tx_Rx同时使用
#define normal_tx 0//正常模式发送设置;0:关闭,1开启.不能与normal_rx同时使用
#define normal_rx 1//正常模式接收设置;0:关闭,1开启.不能与normal_tx同时使用
void LED_Task(void *param);
void CAN1_Task(void *param);
void Delay(u32 count);
#endif
c
//mytask.c
#include "mytask.h"
void Delay(u32 count)
{
u32 i=0,j;
for(;i<count;i++)
for(j=0;j<1000;j++);
}
void LED_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口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC.13
GPIO_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);
}
}
void CAN1_Task(void *param){
#if one_Tx_Rx_status
/*回环模式*/
MyCAN_Init(one_Tx_Rx);
uint8_t Can_Tx_Data1[]={0x66,0x88,0x99};
MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);
// 等待一小段时间(回环模式下发送后几乎立即收到)
for(uint32_t i = 0; i < 100000; i++);
// 检查接收 FIFO
if(CAN_MessagePending(CAN1, CAN_FIFO0) > 0){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[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);
// 检查接收 FIFO
if(CAN_MessagePending(CAN1, CAN_FIFO0) > 0){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[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
#if normal_status
/*正常模式*/
MyCAN_Init(normal);
#if normal_tx
/*正常模式-发送*/
while(1){
uint8_t Can_Tx_Data1[]={0x66,0x88,0x99};
MyCAN_Transmit_standard(0x123,3,Can_Tx_Data1);
vTaskDelay(500);//500ms
}
#endif
#if normal_rx
/*正常模式接收*/
while(1){
uint32_t RxID;
uint8_t RxLen;
uint8_t RxData[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自己填充的)


