基于stm32f103c8t6最小系统板俩块版通讯

文章目录

前提

本文章测试使用的是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收发器自发自收

通过驱动MyCcan.c和MyCan.h来写

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自己填充的)
相关推荐
weixin_456808382 小时前
【沁恒蓝牙开发】从机判断主机是否使能CCCD
单片机·嵌入式硬件
深圳英康仕2 小时前
一款面向AGV智能搬运机器人的RK3588工控机的数据资料整理
嵌入式硬件·rk3588·工控机·agv·智能搬运机器人
fengfuyao9852 小时前
STM32F030 SD卡文件系统读取实例
stm32·单片机·嵌入式硬件
kebidaixu2 小时前
FreeRTOS 移植到 STM32F407VETX 记录(三)
stm32·单片机·嵌入式硬件
普中科技13 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 45 章 FSMC-外扩 SRAM 实验
stm32·单片机·嵌入式硬件·fsmc·普中科技·外扩sram·is62wv51216
xiaoyuchidayuma15 小时前
永磁同步发电机的线电压和直流母线电压的关系
嵌入式硬件
潜创微科技15 小时前
4K60 over IP 方案简介
网络·嵌入式硬件·网络协议·tcp/ip·音视频
rit843249915 小时前
基于C#的USB HID设备读取测试软件
嵌入式硬件
三佛科技-1873661339716 小时前
FT32F103C8AT7兼容GD32F103C8T632 位通用微控制器MCU,替代性分析
单片机·嵌入式硬件