一.CAN协议的基本特点
1.1 特点
1.2 电平标准
总结就是显性电平是0,隐性电平是1,很多单元设备挂在主设备上时,主单元是相当于总线,其他单元只要有一个输出0,总线就显示0,只要其他单元全部输出1,总线就是1
1.3 基本的五个帧
这里最重要的是数据帧,也是最复杂的
1.4 数据帧
串口的一帧率是10-11位,起始位,数据位8位(一字节),(校验位),停止位,而CAN的一帧是七个段组成的,如下图所示,帧起始就是跟串口起始位一样,仲裁段是表示优先级,也就是ID,控制段如下图,一帧数据段最多发送8字节(64位),CRC段就是检查上面的所有段有没有错误,ACK表示收到了正确的段,帧结束就是相当于串口的停止位。
二.数据帧解析
2.1 帧起始和仲裁段
帧起始是一位显性电平0,然后到11位的仲裁段(看蓝色区域,高低电平看ID号,高位在前,低位在后,这里ID11位的话,高7位是不能都是隐形电平1),然后仲裁段后面跟了一个RTR远程请求位0或者1和一个IDE标识符选择请求位0(这里没有显示出来,因为在控制段里面了),RTR如果是0就是数据帧,数据帧的意思就是后面的数据段是有数据的,如果是1,表示这是一个远程帧,远程帧的意思就是后面的数据段是不存在的,DLC控制段也是0的,远程帧用于请求其他节点发送数据帧。上面说的仲裁位都是标准格式,如果需要扩展,在仲裁位的基础下,把原来的RTR替换成SRR替代远程请求位1,IDE变成扩展标识符1。
2.2 控制段
标准格式下,IDE就是上面说的,在控制段里面为0,然后就是R0,发送的话必须是0,接受的话可以是1,再加上控制段DLC(0—8位),扩展格式下,先r1和r0两个保留位,也是发送必须是0,接受可以是1,再加上DLC控制端(0—8位)。
2.3 数据段和CRC段
(1).数据段的标准格式和扩展格式是一样的都是0-64位,然后数据段是从最高位(MSB),开始输出的,跟串口不一样,串口是低位(LSB)先输出,而CAN是高位先输出的。
(2).CRC段也是标准格式和扩展格式一致都是15位加上一位CRC界定符(用于分隔的位),CRC计算方法就是帧起始+仲裁段+控制端+数据段,最后校验得到这个。
2.4 ACK段和帧结束
(1).ACK段的标准格式和扩展格式是一致的,ACK段分为ACK槽和ACK界定,发送单元发给接受单元的数据没有错后(CRC检测没有错误以后),发送单元会接受到接受单元发送的发送单元ACK段11,发送单元接受到了接受单元发的ACK后,会有一个响应,就是接受单元ACK段0。
(2).帧结束的标准格式和扩展格式是一样的,由于7个位的1组成。
三.总线仲裁
(1).必须同时两个或者两个以上的单元发送数据给主单元,才能进行仲裁(优先级),在总线空闲时,最先发送的单元获得发送权,或者同时发送时,则连续输出0(ID)多的单元,则优先发送,如果ID一样,则比较RTR和SRR等位。
(2).例如下图,单元1和单元2同时发送,单元1在红色位置时发送1,而单元2还是发送0,所以,单元1从下一位开始就进入接受状态,单元2优先发送。
四.位时序
(1).位时序就是设置波特率(传输速度),位速率就是一个发送单元发送每秒的位数叫做位速率(传输速度),位速率又分成四个段(如下图一),CAN协议把每个数据位(数据位就是例如串口里面那个起始位,数据位什么的)都分解成了四段,每个段又由若干个Tq的最小时间单位构成,然后我们想设置波特率的话,就要知道位时间(传输每位的时间),想设置位时间的话,就要知道波特率,位时间=1/波特率。
(2).四个段如图二进行解析。
(3).同步段就是有多个连接在总线上的单元通过此段进行时序调整来同时发送和接受数据,时序都是1到0的一个下降沿,或者0到1的一个上升沿,如图三,这些跳变作为时间基准,用于进行时钟同步和数据位的采样,为1Tq。
(4).传播时间段就是总线上的信号传播延迟,接收单元的输入延迟和发送单元的输出延迟,这个传播时间段的时间为总线上的信号传播延迟,接收单元的输入延迟和发送单元的输出延迟的时间的和的2倍,通常是1—8Tq。
(5).相位缓冲段1的作用是当信号边沿不能被包含于同步段中时,可以用它来进行补偿,通常为1-8Tq。
(6).相位缓冲段2的作用就是各个单元以独立时钟工作时,细微的时钟累计起来就会造成误差,它的作用就可以吸收这个误差。可以通过相位缓冲段加减SJW(SJW名为再同步补偿宽度,用于时钟频率偏差,传送延迟等各个单元有同步误差,SJW可以补充此误差的最大值,SJW不属于这四个段的,为1—4Tq)来吸收误差,SJW加大以后允许误差加大,但是通信速度降低,相位缓冲段2通常为2—8Tq。
(7).在STM32上面传播时间段和相位缓冲段1是加在一起的BS1,然后相位缓冲段2是BS2,就只有这两个时间。
(8).图四是位时序的构成,是假设以1位=10Tq的构成,这个采样时间的加大或者减少的最大值就是SJW,也就是SJW可以调节采样点。
(9).我们只需要设置传播时间段,相位缓冲段1,相位缓冲段2和SJW的值,剩下的都是硬件自动去完成。
图一
图二
图三
图四
五.STM32CAN控制器原理与配置
5.1 STM32CAN控制器介绍
对于STM32F407过滤器组有28个,STM32F103就有14个。
5.2 CAN的模式
(1).CAN的模式分为工作模式,测试模式和调试模式,工作模式又分为三个模式,如图一,一开始就需要初始化模式,初始化后,设置正常模式(CAN控制器既可以向总线发也可以接收总线的数据),就可以开始工作了,睡眠模式主要用来降低功耗用的。
(2).测试模式的静默模式是指在STM32的CAN控制器中,静默模式下(如图二),它自己的输出端的逻辑O数据会直接传输到它自己的输入端,逻辑1可以被发送到总线,所以它不能向总线发送显性位(逻辑0),只能发送隐性位(逻辑1)。输入端可以从总线接收内容。由于它只可发送的隐性位不会强制影响总线的状态,所以把它称为静默模式。这种模式一般用于监测,它可以用于分析总线上的流量,但又不会因为发送显性位而影响总线。
(3).在图三中,回环模式下,它自己的输出端的所有内容都直接传输到自己的输入端,输出端的内容同时也会被传输到总线上,即也可使用总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。使用回环模式可以进行自检。
(4).最后在图四中,回环静默模式是以上两种模式的结合,自己的输出端的所有内容都直接传输到自己的输入端,并且不会向总线发送显性位影响总线,不能通过总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。这种方式可以在“热自检”时使用,即自我检查的时候,不会干扰总线。
图一
图二
图三
图四
5.3 CAN框图
(1).下图一中,在STM32F103里面只有互联型产品才有两个CAN,一个是主CAN,另一个是从CAN,其他的只要有一个主CAN,但是在STM32F407里面就有主从CAN。
(2).在图二中,主从CAN都有自己的内核(红色圈),也有各自的主从发送邮箱(蓝圈)和各自的主从接收FIFO(黄圈),都是相互独立的,互不影响,筛选器又称过滤器,STM32F407和STM32F103互联型的才有28个,两个主从CAN是共用一个过滤器的(绿圈),每个CAN的发送邮箱一共是有3个,发送报文的优先级可以使用软件进行控制,还可以记录发送时间,然后每个CAN拥有两个3级深度的接收FIFO,可以使用过滤功能只接收或者不接受某些ID号的报文,可以配置成自动重发,不支持使用DMA进行数据收发。
(3).在图中右下角写了一句话,CAN的开始滤波器编号n是通过写入CAN FMR寄存器的CAN2SB[5:0]配置,这个n是指,比如n是2,那你滤波器就从编号为2到27的硬件过滤器来匹配和过滤CAN消息,过滤器的使用范围并不包括编号为0和1的过滤器。
(4).CAN的内核就是设置那些工作模式什么什么等等,然后我们把需要发的报文发到发送邮箱存着,等到总线空闲就开始发送,有多个单元的话,就根据优先级进行发送,然后就是我们的接受FIFO,总线发送给接受FIFO的数据要结果筛选器(滤波器)才能存到接受FIFO里面,如果总线有很多很多数据要发送,我们节点只要接受某一类的信息数据,这个筛选器就可以筛选掉,就不需要全部收到接受FIFO里面在进行软件筛选。
(5).然后最重要的一点就是如果你只使用从CAN控制器,那也要使能主控制器的时钟,因为是由主CAN控制的。
图一
图二
六 手册寄存器部分讲解
6.1 DBF冻结功能和TTC时间戳
6.2 ABOM自动离线管理和AWUM自动唤醒
6.3 NART自动重传,RFLM锁定模式和TXFP报文发送优先级的判断方法
(1).在下图中的RFLM锁定模式,如果不锁定,每个CAN控制器有六个报文,在六个报文满了以后,他会继续发,0,1,2,3,4,5,他会把0给覆盖了。
(2).然后就是下图的TXFP,报文发送优先级判断,比如发送邮箱0,1,2,我们先存把要发的东西先存进0,再存1,再存2邮箱,然后存进来的ID号是0的优先级最高,然后到2,在到1邮箱,这时候,我们可以控制TXFP选择是先存进来的先发送还是看ID优先级发送。
6.4 波特率设置
(1).BS1就是下图的TS1,然后如果写2就等于2+1(Tq),BS2就等于下图的TS2,也是+1。
(2).一个数据位的时间时间如下图,Tplk=1/f,这里挂载在stm32f103的APB1,由于预分频系数为1,所以时钟为32Mhz。
(3).N就是一个数据位的时间T1bit。
配置的流程,如下图
6.5 发送邮箱
(1.)下图手册的黄线是ID号,也就是STID,然后IDE是标识符选择,选择使用标准标识符11位还是扩展标识符29位,EXID对扩展标识符写的,这里没有使用,就不需要使用扩展标识符,对标识符寄存器中的CAN_TIxR中的TMIDxR_TXRQ置1,这样子邮箱发完数据后,会进行硬件清0。
6.6 接收FIFO
6.7 验收筛选器
(1).STM32F4有28组筛选器(滤波器),STM32F103有14个组筛选器,每组筛选器有2个32位寄存器,每个寄存器可以过滤一个ID号,STM32F103最多就可以滤掉28个ID号。
(2).在图一中,过滤的方法有两种,一种是标识符列表模式,就是要接收的报文列成一个表,要求报文ID与列表中的某一个标识符完全完全相同都可以接收,,它设置5—7组的ID,然后你发的5—7组的ID,正好与接收的要求一样,就可以了,如果6组不一样,就只接收5和7;另一种是掩码模式,意思就是接收报文设置几个关键字,例如设置高四位是1111剩下的是xxxxxxx什么的,只要你高四位是1111,就可以接收。
(3).筛选长度,每个筛选器组由2个32位寄存器组成,然后每个筛选器组可以设置成一个32位筛选器或者两个16位筛选器,然后,在图二中,这一个32位筛选器或者两个16位筛选器和标识符列表模式跟掩码模式又搭配成四种工作模式。
(4).一个32位筛选器和两个16位的筛选器的标识符掩码模式,比如输入的ID号是10101…,然后我们想要10101是关键字的话,就掩码为11111,然后后面的配置为0,就是不需要管后面,关键字就是前面的10101,如图三,然后映射就是要发送的ID,掩码就是设置的关键字,ID号就是过滤器需要过滤的ID号。
(5).两个32位的筛选器的标识符列表模式,,2个寄存器存储的都是要筛选的ID(这里因为一个筛选器组使用32位时就只使用了一个寄存器,所以这里两个寄存器就是两个筛选器组),它只包含2个要筛选的ID值,然后例如筛选器组0和筛选器组1的筛选的ID是6和7的话对应的是6和7就筛选成功,最后在存储进来接收FIFO里面。
图一
图二
图三
七.CAN的结构体设置讲解
7.1 结构体总结
(1).初始化结构体就是设置波特率,工作模式,每个位有多长,构成。
(2).发送及接收结构体设置发送邮箱和接收报文FIFO。
(3).筛选器结构体可以设置筛选器过滤那些报文。
7.2 初始化结构体
(1).在图一中是初始化结构体全部的初始化。
(2).在图二中的参数是工作模式,从上往下分别是正常模式,回环模式,静默模式,回环静默模式。
(3).SJW是在文章四目录里面有记录,是再同步补偿宽度,设置1-4Tq,如图三。
(4).BS1也是文章目录四记录,参数如图四,计算波特率公式也在目录六的6.4。
().TXFP,使能时根据发送的先后顺序为优先级,否则以ID号为优先级判断。
图一
图二
图三
图四
图五
7.3 发送结构体
(1).图一是发送结构体的成员。
(2).想要发送数据时,可以调用图二的函数,第一个参数是选择使用哪个CAN(主从CAN),然后发送的报文就是第二个参数——结构体成员,然后发送数据时,它会存进去邮箱里面,然后在哪个邮箱空闲时,就先存进去哪个邮箱,返回值就是告诉你存进去了哪个邮箱里面——0,1,2邮箱,然后还有一给发送状态的函数,如图四,第一个参数是选择主从CAN,第二个参数是查找哪个邮箱,哪个邮箱就是图二函数的返回值进行查找,如果成功了,这个函数会返回返回值成功或者失败(图四中)。
(3).然后发送结构体的StdId就是标准标识符ID号,ExtId在手册上是扩展标识符ID号,但是这里的结构体ExtId是手册上的标准标识符ID号和扩展标识符ID号加在一起了,一共29位,然后IDE是选择是结构体StdId还是ExtId,在图三中是IDE的参数配置,从上到下是:标准标识符,扩展标识符和标准标识符+扩展标识符。
(4).RTR就是选择是数据帧还是远程帧(具体详解见目录二的2.1),然后图五从上到下的参数分别是数据帧和远程帧。
图一
图二
图三
图四
图五
7.4 接收结构体
(1).图一是接收结构体的成员
(2).如果想要接收报文,就使用图二这个函数,第一个参数选择主从CAN,第二个参数就是选择FIFO的号—0或者1,然后接收的报文就是第三个参数——结构体成员,接收结构体比发送结构体多了一个FMI结构体成员,FMI存储了筛选器的编号,表示是本报文是是经过哪个筛选器存储进来接收FIFO的。
(3).然后怎么知道是哪个FIFO里面有数据呢,就用图三的函数。
图一
图二
图三
7.5 筛选器结构体
(1).图一是筛选器结构体的成员。
(2).图二是筛选器结构体的初始化函数。
(3).在CAN_FxR1和CAN_FxR2寄存器中,结构体成员CAN_FilterIdHight,CAN_FilterIdLow,CAN_FilterMaskIdHight和,CAN_FilterMaskIdLow分别对应图四的红,黑,黄,绿,然后图中的映射是要发送的ID号。
(4).图五是剩下的结构体成员的参数设置。
图一
图二
图四
图五
八.CAN的原理图和接线
这里我们的TJA1050是收发器,如果板子上面没有,需要自己买一个,这里我使用的是STM32f103的正点原子战舰板,需要将跳线帽PA11与CAN_RX,PA12与CAN_TX连接。
九.CAN的回环模式进行自检通过串口发送数据代码
main.c
//一个板子然后自检通过串口发到电脑上显示数据
#include "stm32f10x.h"
#include "UART.h"
#include "CAN.h"
#include "Key.h"
#include "LED.h"
#include "Delay.h"
CanRxMsg CAN_ReceiveData;//CAN的FIFO 0的接收结构体
CanTxMsg CAN_TranData; //发送结构体
uint8_t flag = 0;
uint8_t KeyNum;
uint8_t box;
int main(void)
{
Serial_Init(); //串口初始化
CAN_config();
LEDInit();
Key_Init();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
CAN_TranData.StdId=0; //不是发送标准帧ID,本工程发送扩展帧ID
CAN_TranData.ExtId=PASS_ID; //标准帧ID+扩展帧ID
CAN_TranData.RTR=CAN_RTR_Data;//数据帧(CAN_RTR_Data)—CAN控制器可以进行发送数据,遥控帧(CAN_RTR_Remote)—CAN控制器作为接收数据
CAN_TranData.IDE=CAN_Id_Extended; //选择是扩展ID
CAN_TranData.DLC=1;//存储到邮箱的报文数据长度,0-8,1个长度等于1字节=8位
CAN_TranData.Data[0]=10;//发送的数据,Data[8],8位
box = CAN_Transmit(CAN1,&CAN_TranData);//发送函数,有返回值邮箱号—发给哪个邮箱了
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
Delay_ms(500);
Delay_ms(500);
while(CAN_TransmitStatus(CAN1,box)== CAN_TxStatus_Failed); //判断发送成功还是失败
GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
if(flag == 1)
{
printf("Num :%d\r\n",CAN_TranData.Data[0]);
flag =0;
}
}
}
UART.c
#include "stm32f10x.h" // Device header
#include <stdio.h>
void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch);
return ch;
}
UART.h
#ifndef __UART_H__
#define __UART_H__
#include <stdio.h>
#include "stm32f10x.h" // Device header
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
#endif
CAN.c
#include "stm32f10x.h" // Device header
#include "CAN.h"
/*****
CAN的引脚配置
******/
void CAN_GPIO_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//片上外设(CAN)控制的推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;//TX
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;//RX
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
/*****
CAN的模式配置
******/
void CAN_Mode_Init(void)
{
CAN_InitTypeDef CAN_InitStruct;
CAN_InitStruct.CAN_ABOM=ENABLE; //是否使能离线ABOM离线管理功能
CAN_InitStruct.CAN_AWUM=ENABLE; //是否使能AWUM自动唤醒功能
CAN_InitStruct.CAN_Mode=CAN_Mode_LoopBack; //回环模式
CAN_InitStruct.CAN_NART=ENABLE;// 错误重传
CAN_InitStruct.CAN_RFLM=ENABLE; //是否使能RFLM锁定FIFO功能
CAN_InitStruct.CAN_TTCM=DISABLE; //是否使能TTCM时间戳触发功能
CAN_InitStruct.CAN_TXFP=DISABLE; //配置TXFP报文优先级的判定方法,使能时根据发送的先后顺序为优先级,否则以ID号为优先级判断。
//设置波特率—1M
CAN_InitStruct.CAN_BS1=CAN_BS1_5tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_3tq;
CAN_InitStruct.CAN_Prescaler=4;
CAN_InitStruct.CAN_SJW=CAN_SJW_2tq;
CAN_Init(CAN1,&CAN_InitStruct);
}
/*****
CAN的筛选器配置
******/
void CAN_Filter_Init(void)
{
CAN_FilterInitTypeDef CAN_FilterStruct;
CAN_FilterStruct.CAN_FilterActivation=ENABLE; //是否使能这个过滤器
CAN_FilterStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0; //存储到哪个邮箱
CAN_FilterStruct.CAN_FilterNumber=0; //使用哪个过滤器
CAN_FilterStruct.CAN_FilterScale=CAN_FilterScale_32bit; //配置成多少位
CAN_FilterStruct.CAN_FilterMode=CAN_FilterMode_IdMask;//标识符列表(CAN_FilterMode_IdList)还是标识符掩码(CAN_FilterMode_IdMask),标识符的意思就是ID号
//0x1314=0001 0011 0001 0100,所以32位里面现在就是0000 0000 0000 0000 0001 0011 0001 0100
//标识位(11位)+扩展位=29位,所以现在使用32位的最高三位是000,我们向左移动三位,然后低三位就会补0,低三位就是文章目录六的6.5的发送邮箱标识符寄存器的低三位,其他位就是ID号
//CAN_ID_EXT是第二位IDE的宏定义—扩展帧(因为这里使用了扩展标识符),CAN_RTR_DATA是第一位的宏定义RTR,这里选择了数据帧
//然后我们在目录七的7.5的图四中,我们取的是高16位,所以这里需要&0xFFFF0000,最后在在向右移动16位
CAN_FilterStruct.CAN_FilterIdHigh=((PASS_ID<<3 | CAN_ID_EXT |CAN_RTR_DATA)&0xFFFF0000)>>16; //ID高16位,
CAN_FilterStruct.CAN_FilterIdLow=(PASS_ID<<3 | CAN_ID_EXT |CAN_RTR_DATA)&0xFFFF; //ID低16位
//关键字这里使用全部都一样,正常来说要确定几个既可以了
CAN_FilterStruct.CAN_FilterMaskIdHigh=0xFFFF; //ID高16位,标识符列表就是ID,标识符掩码就是掩码,
CAN_FilterStruct.CAN_FilterMaskIdLow=0XFFFF; //ID低16位标识符列表就是ID,标识符掩码就是掩码
CAN_FilterInit(&CAN_FilterStruct);
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//中断使能信号(NVIC),FIFO 0
}
/*****
CAN的中断配置
******/
void CAN_NVIC_config(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn; //FIFO 0
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
}
/*****
CAN的初始化
******/
void CAN_config(void)
{
CAN_GPIO_Init();
CAN_Mode_Init();
CAN_Filter_Init();
CAN_NVIC_config();
}
extern CanRxMsg CAN_ReceiveData;//CAN的FIFO 0的接收结构体
extern uint8_t flag;
/*****
CAN的FIFO的中断函数
******/
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CAN_Receive(CAN1,CAN_FIFO0,&CAN_ReceiveData);//不需要清0,硬件自动清0
flag = 1;
}
CAN.h
#ifndef __CAN_H__
#define __CAN_H__
#define PASS_ID ((uint32_t)0x1314) //ID号
void CAN_config(void);
#endif
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4) == 0);
Delay_ms(20);
KeyNum = 1;
}
return KeyNum;
}
Key.h
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h" // Device header
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
Delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h" // Device header
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
LED.c文章来源:https://www.toymoban.com/news/detail-775308.html
#include "stm32f10x.h"
#include "LED.h"
void LEDInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOB,GPIO_Pin_5); //初始状态输出为1
}
LED.h文章来源地址https://www.toymoban.com/news/detail-775308.html
#ifndef _LED_H_
#define _LED_H_
void LEDInit(void);
#endif
到了这里,关于STM32——CAN协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!