can通信正如字面意思,利用can的硬件来实现收发,一切工作都是为了接收和发送;
目录
can的基本特性和简介
can的一般说明
can网络结构图
can的框图
一个完整的can必备寄存器要素(如框图示)
can的工作模式流程
分析工作模式流程
睡眠模式
初始化模式
正常模式
进入初始化的目标:(为了进行配置寄存器:主控制、位时序初始化)
进入正常模式,并且能使用的准备工作
筛选器
筛选器的一般说明
配置筛选器的大致流程
筛选器的部分的使用说明:
尺度:
标识符模式:有两种模式
整体效果配,尺度、模式置图
筛选机制
筛选器匹配索引:
筛选机制 框图
接收处理和发送
发送大致状态原理:
发送需要用到的寄存器:
接收大致状态原理
接受需要用到的寄存器:
位定时的配置公式,以及计算设计
附上手工计算:有点不相同但是原理一致;
数据帧
先了解图解规则
数据帧格式:
第一部分理解
第二部分理解
第三部分理解
MCU的can控制器的可以操作的数据帧的段的位:
发送中断
开始接触的问题所在:
发送中断说明:
mobus+can
程序
程序1:环回模式测试
程序2:按键切换模式程序(在程序1基础上)
程序3:添加中断
程序4:大自动部分处理(在程序3的基础上改动)
程序5: 长篇数据发送处理+接收长篇数据处理
mobus+can
略
略
可以看出can收发器独立于mcu外面;而can控制器属于片上外设;can可以实现的自动收发类似于uart串口,只不过can有三个类似uart缓存区(邮箱并且有自动筛选接收);
补充:
头带横杠的,表逻辑0;反之则表示逻辑1
点表示并且,ack表状态,在状态寄存器中会有相应的工作处于状态标记位
SLAK是已经在睡眠模式状态位 ,INAK是已经在初始化状态位;1是在,0未在;
可以看到复位之后就是睡眠模式,所以默认为睡眠模式
睡眠--》正常 :sleep清零 ,inrq清零,并且等待总线为空闲
睡眠--》初始化:sleep清零,inrq置1。并且等待初始状态标记
初始化--》正常:inrq置1,并且等待正常模式状态标记
初始化--》睡眠:sleep置1,inrq清零,并且等待已经睡眠模式标记
正常--》睡眠:sleep置1,等待已经睡眠模式标记;
正常--》初始化:inrq置1,并且等待已经进入初始化模式标记;
为初始化 CAN 控制器,软件必须设置位定时 (CAN_BTR) 和 CAN 选项 (CAN_MCR) 寄存器。
初始化模式下的操作:(按需要配置、不做详细描述)
1、设置位定时 (CAN_BTR)(也称位时序)
模式
同步(也称位时序)
分频(波特率的支持时钟)
************位定时的配置公式,以及计算设计。单独划分一个大标题。 *******************
2、CAN 选项 (CAN_MCR) 寄存器
进入正常模式的请求可通过将 CAN_MCR 寄存器的 INRQ 位清零来发出。bxCAN 进入正常
模式,并与 CAN 总线上的数据传输实现同步后,即可参与总线活动。需要等待出现一个由 11 个连续隐性位(总线空闲状态)组成的序列。(这个应该由硬件完成,我们只需要读取进入正常模式的标记)
软件ps:等待从0计数65535步完成;或者直到读取到正常模式标记;否则再次读取正常模式标记,返回标记;读取到正常模式就已经可以正常使用can总线
筛选就是为了接收功能而存在,可以自动的筛选有用的消息,减轻cpu的负担;滤波器相当于一个数据识别的头帧;筛选出来的,就是所谓的头帧符合的;
筛选器组和筛选器是有区别的,筛选器组可以分成多个筛选器
F4有0-27号的筛选器组,一共28个。一个组有两个寄存器can_fxr0 can_fxr1 ;x代表筛选器组的号数;
手册原文筛选器组尺度和配置模式:为了配置筛选器组,必须通过将 CAN_FAR 寄存器的 FACT 位清零而将其停用。筛选器尺度通过 CAN_FS1R 寄存器的相应 FSCx 位进 行配置,请参见图 231。相应掩码/ 标识符寄存器的标识符列表或标识符掩码模式通过
CAN_FMR 寄存器的 FBMx 位进行配置
- 保证can初始化模式完成,或者保证不在can初始化模式中 (筛选器初始化和can初始化有区别的)
- 进入筛选器的初始化模式 ( 筛选器组主寄存器中 FMR)
- 关闭筛选器 ( 筛选器组激活寄存器中 FAR)
- 尺度配置 ( 筛选器组尺度寄存器中 FS1R)
- 标识符模式配置 ( 筛选器组主寄存器中 FMR)
- 设置筛选器的相应标识符 (筛选i组 寄存器 FiRx x=1、2 )
- 筛选器分配fifo ( 筛选器分配fifo 寄存器 FFA1R )
- 激活筛选器 ( 筛选器激活寄存器中 FAR)
根据筛选器尺度不同,一个筛选器组可以:
● 为 STDID[10:0]、EXTID[17:0]、IDE 和 RTR 位提供一个 32 位筛选器。
● 为 STDID[10:0]、RTR、IDE 和 EXTID[17:15] 位提供两个 16 位筛选器。
在掩码模式下,标识符寄存器与掩码寄存器关联,用以指示标识符的哪些位“必须匹配”,
哪些位“无关”。CAN_FiR0 标识ID CAN_FiR1为掩码
列表模式下:不在使用掩码, CAN_FiR0 标识ID CAN_FiR1标识ID。
在尺度为32位 掩码模式下,构成一个筛选器;
在尺度为32位 列表模式下,构成二个筛选器;
在尺度为16位 掩码模式下,构成2个筛选器;
在尺度为16位 列表模式下,构成4个筛选器;
先认识两个东西,筛选器匹配索引和筛选器优先级规则;通过这两个的说明可以更清楚的了解筛选机制;
能快速的得到有用消息。匹配索引是不固定的,根据筛选器的模式和尺度有关;筛选器组是固定的绑定fifo;(区别筛选器组和筛选器)
图解1、筛选器组是固定方案绑在各自的fifo中
图解2、这是可变的也许能搞出个4个筛选器
筛选器编号就是所谓的索引;下面的机制框图将解释妙用;
筛选器匹配索引的使用方法有两种:(其实我大概明白是一种用索引来找相应的储存信息,比如一个索引4,那么凭借索引4我可以找到存储在在该位置的消息)
● 将筛选器匹配索引与预期值列表进行比较。
● 将筛选器匹配索引用作阵列索引,以访问数据目标位置。
筛选器优先级规则:
根据筛选器组合,可能会出现一个标识符成功通过数个筛选器的情况,不能每个都通过,然后不能每个都记录消息和索引。这种情况下,将根据以下优先级规则选择接收邮箱中存储的筛选器匹配值:
● 32 位筛选器优先于 16 位筛选器。
● 对于尺度相等的筛选器,标识符列表模式优先于标识符掩码模式。
● 对于尺度和模式均相等的筛选器,则按筛选器编号确定优先级(编号越低,优先级
越高)。
解图:
- 为了发送消息,应用程序必须在请求发送前,通过将 CAN_TIxR 寄存器的相应 TXRQ 位置 1,选择一个空发送邮箱,并设置标识符、数据长度代码 (DLC) 和数据
- 邮箱立即进入挂起状 态,等待成为优先级最高的邮箱;
- CAN 总线变为空闲后,被安排好的邮箱中的消息即开始发送(进入发送状态);
- 邮箱 一旦发送成功,即恢复空状态;硬件通过将 CAN_TSR 寄存器的 RQCP 和 TXOK 位置 1,来表示发送成功。如果发送失败,失败原因将由 CAN_TSR 寄存器的 ALST 位(仲裁丢失)和/或 TERR 位(检 测到发送错误)指示。
如果需要中止值得注意的是:如果在邮箱处于发送状态时请求中止,则会出现两种结果;
1、如果邮箱发送成功,将变为空状态,同时 CAN_TSR 寄存器的 TXOK 位置 1 (也就是中止不了)
2、如果发送失败,邮箱变为已安 排状态,发送中止并变为空状态,同时 TXOK 位清零
发送优先级
按标识符
当多个发送邮箱挂起时,发送顺序由邮箱中所存储消息的标识符来确定。根据 CAN 协议的
仲裁,标识符值最低的消息具有最高的优先级。如果标识符值相等,则首先安排发送编号较
小的邮箱。
按发送请求顺序
可以通过设置 CAN_MCR 寄存器中的 TXFP 位,将发送邮箱配置为发送 FIFO。在此模式下,
优先级顺序按照发送请求顺序来确定。该模式对分段发送非常有用。
TME 空标记 RQCP中止状态标记 TXOK是发送完成标记
TXRQ发送请求 ABRQ中止请求;
FMP 记录相应的收到的有效消息的条数的状态
FOVR溢出标记
PS:上溢会根据配置can主寄存器的fifo溢出锁存配置,决定是否覆盖;
详细位在CAN_BTR寄存器中
因为有
BaudRate=1/NominalBitTime
NominalBitTime = tq x(1+tBS1+tBS2)
tBS1 = tq x (TS1[3:0] + 1),
tBS2 = tq x (TS2[2:0] + 1),
tq = (BRP[9:0] + 1) x tPCLK
令n1=(TS1[3:0] + 1) , n2 = (TS2[2:0] + 1) ,n=1+n1+n2 ,A = (BRP[9:0] + 1)
n1最大16 n2最大8 n最大25 A最大1024
所以有NominalBitTime = tq x n;
tq = Ax tPCLK ;其中时钟频率 tPCLK为168M
现在要求波特率为9600
NominalBitTime =1/9600 (s 秒)= tq x n = A/(168*1 000 000) x n
经过计算化简:
17500 = A x n; 其中A最大1024 , n最大25 并且A 和n都必须为整数;
所以现在令A=175 x k 其中k可以为1、2、3、4、5
在保证n最大为25情况下 ,取k=5, A=875 ,n=20;
还原n=20=1+n1+n2 所以n1+n2=19 在范围内任意取值
所以可以设置TS1[3:0]=10 ,TS2[2:0] =7,
还原A=875= (BRP[9:0] + 1) A小于等于1024;
可以取值 BRP[9:0] =874;
还有必须知道灰色是显性 等效于逻辑零;白色是隐性 等效于逻辑1;
半黑半白表示 ,可以逻辑1也可以逻辑0;
蓝色的 是可变的数据
1、注意的是蓝色的线是段的分界线;
例如标准格式的的id 占用12位作为id 段;拓展格式的id 占用32位 作为id段
2、还有必须知道灰色是显性 等效于逻辑零;白色是隐性 等效于逻辑1;
之前的时候一直没注意,所以很难看明白;
ID:高位在前,低位在后。 基本ID,禁止高7位都为隐性,即不能:ID=1111111XXXX。
RTR,远程请求位。0,数据帧;1, 远程帧;
- r0,r1:保留位。必须以显现电平发送,但是接收可以是隐性电平。(这句话一开始难理解,注意r0,r1 不等效任何一个为 ;在标准格式下:r0前面的是IDE ,此时不要妄图以为在拓展模式下的r0前面的r1等价于IDE ,r0和r1是不同模式的一个标记,不是指特定的位)
- DLC:数据长度码。0~8,表示发送/接收的数据长度(字节)。
- IDE,标识符选择位。0,标准标识符;1,扩展标识符;
结论:crc段 ack段 帧结束段;都是不可以主动操作的;或者说单片机是不需要这些段的,或者说这三个段完全由硬件完成(个人那么认为)
分析:
这些寄存器,可以发现以上的寄存器可以找到 数据段 id RTR IDE DLC ;
但是就是没有找到crc ack段 帧结束段 的相关寄存器;
为了发送效率使用发送中断处理发送长篇数据;这样可以更好的保证系统的实时性,也可以保证数据传输的实时性,和整个篇幅的完整性;
使用轮转发送:数据篇幅完整性和时效性
使用等待发送:影响真个系统的实时性
描述:
1、首先一个就是在开启优先级的时候,同时给优先级接收中断和发送中断一起设置;导致了无法接收中断也没有发送中断
2、发送中断:发送邮箱空中断,程序运行无法进入发送邮箱空中断;
解1:一定要一个一个配置在中断优先级,这样才能正常使用;
解2:发送中邮箱空中断,不会因为是空就能触发中断,复位初始化的时候观察发送状态寄存器TSR可以发现一开始所有发送邮箱都是空的;只有发送一次完了之后才会触发中断。所以所谓的发送邮箱空中断称之为发送完成中断;
可以通过图中看出,在使能发送邮箱空的情况下,需要CAN_TSR的RQCP0 1 2其中一个置1才能触发中断,并且完全由硬件触发;所以所谓的发送邮箱空中断,就是邮箱发送完成中断;
所以发送长篇数据大于8 的处理方式:先发送8个字节的数据,剩下的靠发送完成中断衔接发送剩下的数据;
程序部分附上:
在 程序5: 长篇数据发送处理+接收长篇数据处理
不多说了利用can来做mobus协议;
附上程序6:mobus+can程序
#include"main.h"
#include "can.h"
#define can1_rx_enable 0
can1_look_value_struct can1;
void can_init( uint16_t mode ,uint16_t tbs1,uint16_t tbs2 ,uint16_t tsjw ,uint16_t prescaler )
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 ;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz ;
GPIO_Init(GPIOA, & GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11 , GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12 , GPIO_AF_CAN1);
CAN_InitStruct.CAN_ABOM =DISABLE ; // 自动总线关闭管理 ,退出关闭状态时需要信号,且主动软触发
CAN_InitStruct.CAN_AWUM =DISABLE ; //禁止自动唤醒(硬件产生中断,禁止唤醒),由软件触发
CAN_InitStruct.CAN_NART = ENABLE; //禁止自动重复发送
CAN_InitStruct.CAN_RFLM = DISABLE ;
CAN_InitStruct.CAN_TTCM = DISABLE ; //禁止时间戳
CAN_InitStruct.CAN_TXFP = DISABLE ;
CAN_InitStruct.CAN_Mode = mode;
CAN_InitStruct.CAN_BS1= tbs1;
CAN_InitStruct.CAN_BS2= tbs2;
CAN_InitStruct.CAN_SJW= tsjw;
CAN_InitStruct.CAN_Prescaler= prescaler;
CAN_Init( CAN1, &CAN_InitStruct);
CAN_FilterInitStruct.CAN_FilterNumber = 0;
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask ;
CAN_FilterInitStruct.CAN_FilterScale =CAN_FilterScale_32bit ;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0 ;
CAN_FilterInitStruct.CAN_FilterIdLow = 0x0000 ;
CAN_FilterInitStruct.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStruct);
#if can1_rx_enable
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = CAN1_RX0_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(& NVIC_InitStruct);
#endif
}
#if can1_rx_enable
void CAN1_RX0_IRQHandler()
{
CanRxMsg RxMessage ;
if(CAN_GetFlagStatus( CAN1, CAN_FLAG_FMP0))
{
can1.can1_rx.rx_times++;
// if(CAN_MessagePending( CAN1, CAN_FIFO0))
CAN_Receive( CAN1, CAN_FIFO0, & RxMessage);
// CAN_FIFORelease(CAN1, CAN_FIFO0);
// CAN_FIFORelease(CAN1,CAN_FIFO0);
// memcpy( can1.can1_rx.rx_buff,RxMessage.Data,sizeof(RxMessage.Data));
}
CAN_ClearFlag(CAN1, CAN_FLAG_FMP0);
}
#endif
uint8_t can1_send( uint8_t *p_send_buff ,uint8_t len )
{
uint16_t i;
CanTxMsg TxMessage;
/* 给邮箱准备好数据帧的数据 */
TxMessage.StdId = 0x12;
TxMessage.ExtId = 0x12;
TxMessage.IDE = CAN_Id_Standard;
TxMessage.RTR = CAN_RTR_Data ;
TxMessage.DLC = 8;
for(i=0 ; i < len ; i++)
TxMessage.Data[i] = p_send_buff[i]; /**/
CAN_Transmit(CAN1, & TxMessage);/* 给有邮箱挂起 */
i=0;
while( ( !CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok) ) && i < 0xffff ) i++;/*等待传输完成*/
if(CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok))return 1;/*发送成功 返回1 失败0*/
else return 0;
}
uint8_t can1_receive( )
{
CanRxMsg RxMessage ;
if(CAN_MessagePending(CAN1, CAN_FIFO0 ) == 0){return 0; } /* 如果邮箱没有消息,退出*/
CAN_Receive(CAN1, CAN_FIFO0, & RxMessage); /*有消息,接收消息*/
// CAN_FIFORelease( CAN1, CAN_FIFO0); /*释放 邮箱 库函数里面已经释放过一次 */
memcpy( can1.can1_rx.rx_buff,RxMessage.Data,sizeof(RxMessage.Data));
return RxMessage.DLC; /*返回数据帧的数据段长度*/
}
主程序:波特率500k,环回模式;
#include "main.h"
value_struct te = { .init_prescaler=168,
.init_reload=60000
};
double time_us;
int main(void)
{
u8 key,flag; //保存键值
delay_init(168); //初始化延时函数
uart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init(); //初始化LED端口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
// LED0=0; //先点亮红灯
// Time14_pwm_init( 168 ,1000);
// capture_time5_init( 1 ,time5_reload );
// input_pwm_time9_init( te.init_prescaler ,te.init_reload ) ;//168 1000 000
// change_PWM_value( 50 );
can_init( CAN_Mode_LoopBack ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,环回模式
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case WKUP_PRES: //控制蜂鸣器
BEEP=!BEEP;
break;
case KEY0_PRES:{ //控制LED0翻转
//LED0=!LED0;
uint8_t temp[10]={10,9,8,7,6,5,4,3,2,1};
can1_send( temp ,5 ); //发送数组
break;
}
case KEY1_PRES: //控制LED1翻转
LED1=!LED1;
break;
case KEY2_PRES: //同时控制LED0,LED1翻转
LED0=!LED0;
LED1=!LED1;
break;
}
}else delay_ms(10);
if( ( flag = can1_receive() ) ) //如果接收到,串口打印数据
{
int i;
for( i = 0; i < flag; i++ )
{
printf("%d ",can1.can1_rx.rx_buff[i]);
}
}
// Link_captrue_peroid_and_hight( &te.PWM_captrue_result.higth_time ,&te.PWM_captrue_result.period_time ,5 );
}
}
效果:环回模式下500k波特率下,按键0按下发送,然后接收到就串口打印出来;
#include "main.h"
value_struct te = { .init_prescaler=168,
.init_reload=60000
};
double time_us;
int main(void)
{
u8 key,flag; //保存键值
static bool cnt;
delay_init(168); //初始化延时函数
uart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init(); //初始化LED端口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
// LED0=0; //先点亮红灯
// Time14_pwm_init( 168 ,1000);
// capture_time5_init( 1 ,time5_reload );
// input_pwm_time9_init( te.init_prescaler ,te.init_reload ) ;//168 1000 000
// change_PWM_value( 50 );
can_init( CAN_Mode_LoopBack ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,环回模式
while(1)
{
key=KEY_Scan(0); //得到键值
if(key)
{
switch(key)
{
case WKUP_PRES: //控制蜂鸣器
BEEP=!BEEP;
break;
case KEY0_PRES:{ //控制LED0翻转
//LED0=!LED0;
uint8_t temp[10]={10,9,8,7,6,5,4,3,2,1};
can1_send( temp ,5 ); //发送数组
break;
}
case KEY1_PRES: //控制LED1翻转
{
LED1=!LED1;
cnt =!cnt;//改变模式的模式
if(cnt)
{
can_init( CAN_Mode_LoopBack ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,环回模式
printf("环回模式 ");
}else
{
can_init( CAN_Mode_Normal ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,正常模式
printf("正常模式 ");
}
break;
}
case KEY2_PRES: //同时控制LED0,LED1翻转
LED0=!LED0;
LED1=!LED1;
break;
}
}else delay_ms(10);
if( ( flag = can1_receive() ) ) //如果接收到,串口打印数据
{
int i;
if(cnt)
{
printf("环回模式 ");
}
else
{
printf("正常模式 ");
can1_send( can1.can1_rx.rx_buff ,flag ); //在正常模式下,接收就发送;
}
for( i = 0; i < flag; i++ ) //打印接收的出来
{
printf("%d ",can1.can1_rx.rx_buff[i]);
}
printf("\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n ");
}
// Link_captrue_peroid_and_hight( &te.PWM_captrue_result.higth_time ,&te.PWM_captrue_result.period_time ,5 );
}
}
改动:改了主程序,增加按键模式切换。只有在正常模式下,接收就发送出去;
效果:在can盒调试工具的情况下发送,之后可以看到接收的也是发送;
一旦监测到 128 次连续 11 个隐性位,即通过硬件非自动退出总线关闭状态。
一旦监测到 CAN 消息,即通过硬件非自动退出睡眠模式。
在帧起始不捕获时间戳
ps:中断函数入口只能选void CAN1_SCE_IRQHandler();其他都对不上
#include"main.h"
#include "can.h"
#define can1_rx_enable 0
can1_look_value_struct can1;
void can_init( uint16_t mode ,uint16_t tbs1,uint16_t tbs2 ,uint16_t tsjw ,uint16_t prescaler )
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 ;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz ;
GPIO_Init(GPIOA, & GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11 , GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12 , GPIO_AF_CAN1);
CAN_InitStruct.CAN_ABOM =DISABLE ; // 自动总线关闭管理 ,退出关闭状态时需要信号就行,需要主动进入初始化模式
CAN_InitStruct.CAN_AWUM =DISABLE ; //不自动唤醒,需要软件触发清除睡眠
CAN_InitStruct.CAN_NART = ENABLE; //禁止自动重复发送
CAN_InitStruct.CAN_RFLM = DISABLE ; //禁止fifo锁定模式,消息满就覆盖
CAN_InitStruct.CAN_TTCM = DISABLE ; //不捕获帧起始时间戳
CAN_InitStruct.CAN_TXFP = DISABLE ; //消息优先级取决于标识符
CAN_InitStruct.CAN_Mode = mode;
CAN_InitStruct.CAN_BS1= tbs1;
CAN_InitStruct.CAN_BS2= tbs2;
CAN_InitStruct.CAN_SJW= tsjw;
CAN_InitStruct.CAN_Prescaler= prescaler;
CAN_Init( CAN1, &CAN_InitStruct);
CAN_FilterInitStruct.CAN_FilterNumber = 0;
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask ;
CAN_FilterInitStruct.CAN_FilterScale =CAN_FilterScale_32bit ;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0 ;
CAN_FilterInitStruct.CAN_FilterIdLow = 0x0000 ;
CAN_FilterInitStruct.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStruct);
#if can1_rx_enable
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = CAN1_RX0_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(& NVIC_InitStruct);
#endif
CAN_ITConfig(CAN1, CAN_IT_WKU | CAN_IT_BOF , ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = CAN1_SCE_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(& NVIC_InitStruct);
}
void CAN1_SCE_IRQHandler()
{
if(CAN_GetFlagStatus( CAN1, CAN_FLAG_WKU))
{
CAN_WakeUp( CAN1);
}
if(CAN_GetFlagStatus( CAN1, CAN_FLAG_BOF))
{
CAN_OperatingModeRequest(CAN1, CAN_OperatingMode_Initialization);
CAN_OperatingModeRequest(CAN1, CAN_OperatingMode_Normal);
}
CAN_ClearFlag(CAN1, CAN_FLAG_WKU |CAN_FLAG_BOF);
}
#if can1_rx_enable
void CAN1_RX0_IRQHandler()
{
CanRxMsg RxMessage ;
if(CAN_GetFlagStatus( CAN1, CAN_FLAG_FMP0))
{
can1.can1_rx.rx_times++;
// if(CAN_MessagePending( CAN1, CAN_FIFO0))
CAN_Receive( CAN1, CAN_FIFO0, & RxMessage);
// CAN_FIFORelease(CAN1, CAN_FIFO0);
// CAN_FIFORelease(CAN1,CAN_FIFO0);
// memcpy( can1.can1_rx.rx_buff,RxMessage.Data,sizeof(RxMessage.Data));
}
CAN_ClearFlag(CAN1, CAN_FLAG_FMP0);
}
#endif
uint8_t can1_send( uint8_t *p_send_buff ,uint8_t len )
{
uint16_t i;
CanTxMsg TxMessage;
/* 给邮箱准备好数据帧的数据 */
TxMessage.StdId = 0x12;
TxMessage.ExtId = 0x12;
TxMessage.IDE = CAN_Id_Standard;
TxMessage.RTR = CAN_RTR_Data ;
TxMessage.DLC = len;
for(i=0 ; i < len ; i++)
TxMessage.Data[i] = p_send_buff[i]; /**/
CAN_Transmit(CAN1, & TxMessage);/* 给有邮箱挂起 */
i=0;
while( ( !CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok) ) && i < 0xffff ) i++;/*等待传输完成*/
if(CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok))return 1;/*发送成功 返回1 失败0*/
else return 0;
}
uint8_t can1_receive( )
{
CanRxMsg RxMessage ;
if(CAN_MessagePending(CAN1, CAN_FIFO0 ) == 0){return 0; } /* 如果邮箱没有消息,退出*/
CAN_Receive(CAN1, CAN_FIFO0, & RxMessage); /*有消息,接收消息*/
CAN_FIFORelease( CAN1, CAN_FIFO0); /*释放 邮箱 */
memcpy( can1.can1_rx.rx_buff,RxMessage.Data,sizeof(RxMessage.Data));
return RxMessage.DLC; /*返回数据帧的数据段长度*/
}
改动
效果:没有验证中断
一旦监测到 128 次连续 11 个隐性位,即通过硬件自动退出总线关闭状态。
一旦监测到 CAN 消息,即通过硬件自动退出睡眠模式。
在帧起始捕获时间戳
CAN_InitStruct.CAN_ABOM =ENABLE ; // 自动总线关闭管理 ,退出关闭状态时需要信号就行,不需要主动进入初始化模式
CAN_InitStruct.CAN_AWUM =ENABLE ; //自动唤醒,不需要软件触发清除睡眠
CAN_InitStruct.CAN_NART = ENABLE; //禁止自动重复发送
CAN_InitStruct.CAN_RFLM = DISABLE ; //禁止fifo锁定模式,消息满就覆盖
CAN_InitStruct.CAN_TTCM = ENABLE ; //捕获帧起始时间戳
CAN_InitStruct.CAN_TXFP = DISABLE ; //消息优先级取决于标识符
并且删除:void CAN1_SCE_IRQHandler();
改动:如上代码段
效果:
can源文件:
#include"main.h"
#include "can.h"
#include "string.h"
can1_look_value_struct can1;
#define len_rx_buff 255
#define len_tx_buff 255
static bool tx_over_flag;
static uint8_t p_rx_lenght,p_tx_lenght;
static uint8_t rx_buff[ len_rx_buff ] ,tx_buff[ len_tx_buff ];
volatile uint32_t *p_can1_TSR;
void can_init( uint16_t mode ,uint16_t tbs1,uint16_t tbs2 ,uint16_t tsjw ,uint16_t prescaler )
{
GPIO_InitTypeDef GPIO_InitStruct;
CAN_InitTypeDef CAN_InitStruct;
CAN_FilterInitTypeDef CAN_FilterInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA , ENABLE);
RCC_APB1PeriphClockCmd( RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 ;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF ;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz ;
GPIO_Init(GPIOA, & GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11 , GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12 , GPIO_AF_CAN1);
CAN_InitStruct.CAN_ABOM =ENABLE ; // 自动总线关闭管理 ,需要关闭信号,且主动软触发
CAN_InitStruct.CAN_AWUM =ENABLE ; //禁止自动唤醒(硬件产生中断,禁止唤醒),由软件触发
CAN_InitStruct.CAN_NART = ENABLE; //禁止自动重复发送
CAN_InitStruct.CAN_RFLM = ENABLE ;
CAN_InitStruct.CAN_TTCM = DISABLE ; //禁止时间戳
CAN_InitStruct.CAN_TXFP = DISABLE ;
CAN_InitStruct.CAN_Mode = mode;
CAN_InitStruct.CAN_BS1= tbs1;
CAN_InitStruct.CAN_BS2= tbs2;
CAN_InitStruct.CAN_SJW= tsjw;
CAN_InitStruct.CAN_Prescaler= prescaler;
CAN_Init( CAN1, &CAN_InitStruct);
CAN_FilterInitStruct.CAN_FilterNumber = 0;
CAN_FilterInitStruct.CAN_FilterMode = CAN_FilterMode_IdMask ;
CAN_FilterInitStruct.CAN_FilterScale =CAN_FilterScale_32bit ;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0 ;
CAN_FilterInitStruct.CAN_FilterIdLow = 0x0000 ;
CAN_FilterInitStruct.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStruct.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStruct);
CAN_ITConfig(CAN1, CAN_IT_FMP0 /*|CAN_IT_TME*/, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = CAN1_RX0_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(& NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = CAN1_TX_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2 ;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init(& NVIC_InitStruct);
p_can1_TSR = &CAN1->TSR;//发送邮箱空状态
}
void CAN1_RX0_IRQHandler()
{
if(CAN_GetITStatus( CAN1, CAN_IT_FMP0))
{
can_recive_interrup();
}
if(CAN_GetITStatus( CAN1, CAN_IT_TME))
{
can_send_interrup();
}
CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0|CAN_IT_TME);
}
void CAN1_TX_IRQHandler()
{
if(CAN_GetITStatus( CAN1, CAN_IT_TME))
{
can_send_interrup();
}
CAN_ClearITPendingBit(CAN1, CAN_IT_TME);
}
/* */
uint8_t can1_send( uint8_t *p_send_buff ,uint8_t len )
{
uint16_t i;
CanTxMsg TxMessage;
/* 给邮箱准备好数据帧的数据 */
TxMessage.StdId = 0x12;
TxMessage.ExtId = 0x12;
TxMessage.IDE = CAN_Id_Standard;
TxMessage.RTR = CAN_RTR_Data ;
TxMessage.DLC = len;
for(i=0 ; i < len ; i++)
TxMessage.Data[i] = p_send_buff[i]; /**/
CAN_Transmit(CAN1, & TxMessage);/* 给有邮箱挂起 */
/** 这几行代码屏蔽掉 ,用发送邮箱空中断衔接剩下的发送 ***/
// {
//
// i=0;
// while( ( !CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok) ) && i < 0xffff ) i++;/*等待传输完成*/
//
// if(CAN_TransmitStatus(CAN1, CAN_TxStatus_Ok))return 1;/*发送成功 返回1 失败0*/
// else return 0;
// }
}
uint8_t can1_receive( )
{
CanRxMsg RxMessage ;
if(CAN_MessagePending(CAN1, CAN_FIFO0 ) == 0){return 0; } /* 如果邮箱没有消息,退出*/
CAN_Receive(CAN1, CAN_FIFO0, & RxMessage); /*有消息,接收消息*/
// CAN_FIFORelease( CAN1, CAN_FIFO0); /*释放 邮箱 */
memcpy( can1.can1_rx.rx_buff,RxMessage.Data,sizeof(RxMessage.Data));
return RxMessage.DLC; /*返回数据帧的数据段长度*/
}
//双重指针的目标是,方便调用的时候重新改变单指针地址
//指针相当于门牌1,门牌房子里面又有一个门牌2。这时候是把里面的门牌2上面的文字改变;
//所以在A源文件调用这个函数,是为了让A源文件的门牌2 得 B源文件(B是此文件)的门牌号
void Drv_can_get_recive_buff( uint8_t **rxlen , uint8_t **txlen ,uint8_t **rxbuff ,uint8_t **txbuff )
{
*rxlen = &p_rx_lenght;
*txlen = &p_tx_lenght;
*rxbuff = rx_buff;
*txbuff = tx_buff;
}
void Drv_can_send_buff() //发送驱动函数
{
if( p_tx_lenght > 0 && tx_over_flag == 1 )
{
int temp=0;
if(p_tx_lenght > 8 )
{
CAN_ITConfig(CAN1, CAN_IT_TME, ENABLE); //有长度就使能发送中断
temp=8;
}
else
{
temp= p_tx_lenght;
}
can1_send( tx_buff ,temp ); //发送数据
}
}
//给其他源文件,驱动此源文件can的一个链接函数
void Link_can1_send( uint8_t len ,uint8_t enable )
{
uint8_t i,*mian_temp=tx_buff;
p_tx_lenght = len ;
tx_over_flag = enable ;
for( i =0 ;i<255;i++)
{ mian_temp[ i ] = i; }
// can1_send( mian_temp , 8);
Drv_can_send_buff();
}
void can_send_interrup()
{
static uint8_t i = 8,counter; //能进中断是因为已经发了8长度。还需要继续发,所以i必须等于8
if(i < p_tx_lenght && p_tx_lenght < len_tx_buff)
{
tx_over_flag = 0;
if( p_tx_lenght > i+8 ) //计算发送长度 counter
{
counter = 8;
}
else
{
counter = p_tx_lenght - i;
}
can1_send( &tx_buff[i] , counter); //发送长度counter的消息
i=i+ counter ; //累加已经发送的长度
}else //发送所有数据完成后,恢复所有数据和禁止发送完成中断
{
tx_over_flag=1;
i= 8 ; // 恢复到8 ,每次发送的大于8的长数据 只要是能进中断都是因为数据大于8,保证中断后从数据长8后面发
counter=0;
p_tx_lenght = 0;
CAN_ITConfig(CAN1, CAN_IT_TME, DISABLE); //发送完成先清清零数据,禁止can发送空中断
}
}
void can_recive_interrup()
{
CanRxMsg RxMessage ;
if( p_rx_lenght
主函数源文件:
#include "main.h"
uint8_t *mian_temp;
double time_us;
int main(void)
{
u8 key,flag ,i; //保存键值
static bool cnt;
// delay_init(168); //初始化延时函数
software_times_base_init( 168 );
uart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init(); //初始化LED端口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
LED1=!LED1;
LED0=!LED0;
can_init( CAN_Mode_Normal ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,环回模式
while(1)
{
LED1=!LED1;
LED0=!LED0;
Link_can1_send( 15 ,ENABLE ); //驱动can测试的链接函数
delay_Ms_Block( 5000);
// Link_modbus();
// Link_captrue_peroid_and_hight( &te.PWM_captrue_result.higth_time ,&te.PWM_captrue_result.period_time ,5 );
}
}
效果:can盒子调试助手,上面会每5秒收到两个数据帧,一共15个字节的数据 从0到15;
在程序5的基础上进行增加mubus协议;
协议在另一篇文章上具体有;
主程序:
#include "main.h"
uint8_t *mian_temp;
double time_us;
int main(void)
{
u8 key,flag ,i; //保存键值
static bool cnt;
// delay_init(168); //初始化延时函数
software_times_base_init( 168 );
uart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init(); //初始化LED端口
BEEP_Init(); //初始化蜂鸣器端口
KEY_Init(); //初始化与按键连接的硬件接口
LED1=!LED1;
LED0=!LED0;
can_init( CAN_Mode_Normal ,CAN_BS1_7tq ,CAN_BS2_6tq ,CAN_SJW_1tq ,6 ); //波特率500k,环回模式
while(1)
{
LED1=!LED1;
LED0=!LED0;
// Link_can1_send( 15 ,ENABLE ); //驱动can测试的链接函数
delay_Ms_Block( 5000);
Link_modbus();//mubus协议链接程序
// Link_captrue_peroid_and_hight( &te.PWM_captrue_result.higth_time ,&te.PWM_captrue_result.period_time ,5 );
}
}
效果:can盒子工具;发送协议指令:测试数据 55 08 01 64 00 03 ed fd
返回串九个字节的响应;(响应是我自己的协议,自己设置的)