一、2个或以上同学相互连接,利用CAN通信,向对方发送带有本人姓名的信息。连线方式:按基本原理性电路(不带收发器芯片)连接,参考教材图10-1。
部分代码:
main.c(向对方发送自己的
if (mFlag=='L') //判断灯的状态标志
{
mLightCount++;
mFlag='A'; //灯的状态标志
gpio_set(LIGHT_RED,LIGHT_ON); //灯“亮”
//【***CAN模块发送一帧数据***】
if(can_send(CAN_1, txMsgID, 8, (uint8_t*)"IamLYY") != 0) printf("failed\r\n");
printf("\n");
}
连接原理图:
连接图分析:
在这个系统中,存在三个CAN节点(节点1、节点2、节点3),这些节点通过一个共享的总线进行通信。CAN总线的设计使其具备较高的通信可靠性和容错能力。
每个CAN节点通过两个端口与总线相连:CANRX用于接收来自总线的数据,CANTX用于向总线发送数据。同时,每个节点都有一个与地(GND)相连的3KΩ电阻,用于匹配总线阻抗和提供差分信号的参考电平。此外,每个节点还有一个+5V电源,为节点电路提供所需的电压。
在CAN总线上,数据通过差分信号进行传输,即CANH和CANL两条线。这两条线的电压差表示了传输的数据。当节点需要发送数据时,它会在CANTX端口上产生相应的差分信号,并将其发送到总线上。同时,节点会在CAN RX端口上监听总线上的信号,以接收来自其他节点的数据。
由于CAN总线是共享的,因此多个节点可以同时发送数据。然而,为了避免数据冲突,CAN总线使用了一种基于优先级的仲裁机制,确保只有具有最高优先级的节点能够在任一时刻发送数据。这种机制使得CAN总线能够支持多主通信,即多个节点都可以作为主节点来发送数据。
CAN原理:
(1)以广播的形式发送报文。当CAN总线上的某个节点需要给其他节点发送消息时,会以广播的形式发送给总线上所有的节点,因为总线上的节点不适用地址来进行配置CAN系统,而是根据报文的开头的11位标识符决定是否要接受其他节点发来的报文;
(2)每个节点都有自己的处理器和CAN总线接口控制器。
(3)当一个节点需要发送数据到另一个节点时,自身节点的处理器需要将要发送的数据和自己的标识符传给自身的总线控制接口,处于准备状态;当获取到总线的使用权后,将数据和标识符组装成报文,将报文以一定格式发出,此时其他的节点处于接收状态.至于其他节点是否接收,由其他节点决定,是都会对某些报文进行过滤;
(4)当新增的节点仅仅是纯粹的数据接收设备时,只需要该设备直接从总线上接收数据即可。
can.c加上注释:
#include "can.h"
CAN_TypeDef *CAN_ARR[] = {(CAN_TypeDef*)CAN1_BASE};
IRQn_Type table_irq_can[2] = {CAN1_RX0_IRQn, CAN1_RX1_IRQn};
uint8_t can_send_once(uint8_t canNo, uint32_t DestID, uint16_t len ,uint8_t* buff);
uint8_t CAN_HWInit(uint8_t CANChannel);
uint8_t CAN_SWInit_Entry(uint8_t canNo);
void CAN_SWInit_CTLMode(uint8_t canNo);
void CAN_SWInit_BT(uint8_t canNo, uint32_t CANMode, uint32_t Prescaler);
uint8_t CAN_SWInit_Quit(uint8_t canNo);
uint8_t CANFilterConfig(uint8_t canNo, uint32_t canID, uint32_t FilterBank, uint32_t Can_Rx_FifoNo, uint8_t IsActivate, uint32_t FilterMode, uint32_t FilterScale);
//=====================================================================
//函数名称:can_init
//函数返回:无
//参数说明:canNo:模块号,本芯片只有CAN_1
// canID:自身CAN节点的唯一标识,例如按照CANopen协议给出
// BitRate:位速率
//功能概要:初始化CAN模块
//=====================================================================
void can_init(uint8_t canNo, uint32_t canID, uint32_t BitRate)
{
//声明Init函数使用的局部变量
uint32_t CANMode;
uint32_t CANFilterBank;
uint32_t CANFiltermode;
uint32_t CAN_Filterscale;
//给Init函数使用的局部变量赋初值
CANMode = CAN_MODE_NORMAL; //2024.6设置CAN控制器为正常模式,允许正常通信
CANFilterBank = CANFilterBank0;
CANFiltermode = CAN_FILTERMODE_IDMASK;
CAN_Filterscale = CAN_FILTERSCALE_32BIT;
//(1)CAN总线硬件初始化
CAN_HWInit(CAN_CHANNEL);
//(2)CAN总线进入软件初始化模式
CAN_SWInit_Entry(canNo);
//(3)CAN总线模式设置
CAN_SWInit_CTLMode(canNo);
//(4)CAN总线位时序配置
CAN_SWInit_BT(canNo,CANMode,BitRate);
//(5)CAN总线过滤器初始化
CANFilterConfig(canNo, canID, CANFilterBank, CAN_RX_FIFO0, 1, CANFiltermode, CAN_Filterscale);
//(6)CAN总线退出软件初始化模式,进入正常模式
CAN_SWInit_Quit(canNo);
}
//=====================================================================
//函数名称:can_send
//函数返回:0=正常,1=错误
//参数说明:canNo:模块号,本芯片只有CAN_1
// DestID:目标CAN节点的唯一标识,例如按照CANopen协议给出
// len:待发送数据的字节数
// buff:待发送数据发送缓冲区首地址
//功能概要:CAN模块发送数据
//=====================================================================
uint8_t can_send(uint8_t canNo, uint32_t DestID, uint16_t len ,uint8_t* buff)
{
if(DestID > 0x1FFFFFFFU) return 1;
uint8_t send_length;
for(int i = len; i > 0; i = i-8)
{
send_length = (i>8)?8:i;
if(can_send_once(canNo,DestID,send_length,buff+len-i) == 1)//2024.6尝试发送CAN消息,如果发送失败(返回1表示失败),则整个发送过程也失败
{
return 1;
}
}
return 0;
}
//=====================================================================
//函数名称:can_recv
//函数返回:接收到的字节数
//参数说明:canNo:模块号,本芯片只有CAN_1
// buff:接收到的数据存放的内存区首地址
//功能概要:在CAN模块接收中断中调用本函数接收已经到达的数据
//=================================================================&