can通信笔记

can通信正如字面意思,利用can的硬件来实现收发,一切工作都是为了接收和发送;

目录

can的基本特性和简介

can的一般说明

can网络结构图 

can的框图

一个完整的can必备寄存器要素(如框图示)

can的工作模式流程

分析工作模式流程

睡眠模式

初始化模式

正常模式

进入初始化的目标:(为了进行配置寄存器:主控制、位时序初始化)

进入正常模式,并且能使用的准备工作

筛选器

筛选器的一般说明

配置筛选器的大致流程

筛选器的部分的使用说明:

尺度:

标识符模式:有两种模式

整体效果配,尺度、模式置图

 筛选机制

筛选器匹配索引:

筛选机制      框图

接收处理和发送

发送大致状态原理:

 发送需要用到的寄存器:

接收大致状态原理

接受需要用到的寄存器:

位定时的配置公式,以及计算设计

附上手工计算:有点不相同但是原理一致;

数据帧

先了解图解规则

数据帧格式:

第一部分理解 

第二部分理解

 第三部分理解

MCU的can控制器的可以操作的数据帧的段的位:

发送中断

开始接触的问题所在:

发送中断说明:

mobus+can

程序

 程序1:环回模式测试

 程序2:按键切换模式程序(在程序1基础上)

 程序3:添加中断

 程序4:大自动部分处理(在程序3的基础上改动)

程序5: 长篇数据发送处理+接收长篇数据处理

mobus+can


can的基本特性和简介

can的一般说明

can网络结构图 

可以看出can收发器独立于mcu外面;而can控制器属于片上外设;can可以实现的自动收发类似于uart串口,只不过can有三个类似uart缓存区(邮箱并且有自动筛选接收);

can通信笔记_第1张图片

can的框图

一个完整的can必备寄存器要素(如框图示)

  • 主控制寄存器
  • 主状态寄存器
  • 位时序寄存器
  • 筛选器
  • fifo 收发  以及等等略

can通信笔记_第2张图片

can的工作模式流程

can通信笔记_第3张图片

 补充:

头带横杠的,表逻辑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)(也称位时序)

模式

  • 位 31 SILM:静默模式(调试)(Silent mode (debug))
  • 位 30 LBKM:环回模式(调试)(Loop back mode (debug))

同步(也称位时序)

  • 位 25:24 SJW[1:0]:再同步跳转宽度 (Resynchronization jump width)
  • 位 22:20 TS2[2:0]:时间段 2 (Time segment 2)
  • 位 19:16 TS1[3:0]:时间段 1 (Time segment 1)

分频(波特率的支持时钟)

  • 位 9:0 BRP[9:0]:波特率预分频器 (Baud rate prescaler)

************位定时的配置公式,以及计算设计。单独划分一个大标题。     *******************

2、CAN 选项 (CAN_MCR) 寄存器

  • 位 7 TTCM:时间触发通信模式 (Time triggered communication mode)
  • 位 6 ABOM:自动的总线关闭管理 (Automatic bus-off management)
  • 位 5 AWUM:自动唤醒模式 (Automatic wakeup mode)
  • 位 4 NART:禁止自动重发送 (No automatic retransmission)
  • 位 3 RFLM:接收 FIFO 锁定模式 (Receive FIFO locked mode)
  • 位 2 TXFP:发送 FIFO 优先级 (Transmit FIFO priority)

进入正常模式,并且能使用的准备工作

进入正常模式的请求可通过将 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个筛选器;

can通信笔记_第4张图片

 筛选机制

先认识两个东西,筛选器匹配索引筛选器优先级规则;通过这两个的说明可以更清楚的了解筛选机制;

筛选器匹配索引:

能快速的得到有用消息。匹配索引是不固定的,根据筛选器的模式和尺度有关;筛选器组是固定的绑定fifo;(区别筛选器组和筛选器)

图解1、筛选器组是固定方案绑在各自的fifo中

图解2、这是可变的也许能搞出个4个筛选器

can通信笔记_第5张图片

筛选器编号就是所谓的索引;下面的机制框图将解释妙用;

筛选器匹配索引的使用方法有两种:(其实我大概明白是一种用索引来找相应的储存信息,比如一个索引4,那么凭借索引4我可以找到存储在在该位置的消息)
● 将筛选器匹配索引与预期值列表进行比较。
● 将筛选器匹配索引用作阵列索引,以访问数据目标位置。

筛选器优先级规则:

根据筛选器组合,可能会出现一个标识符成功通过数个筛选器的情况,不能每个都通过,然后不能每个都记录消息和索引。这种情况下,将根据以下优先级规则选择接收邮箱中存储的筛选器匹配值:
● 32 位筛选器优先于 16 位筛选器。
● 对于尺度相等的筛选器,标识符列表模式优先于标识符掩码模式。
● 对于尺度和模式均相等的筛选器,则按筛选器编号确定优先级(编号越低,优先级 
越高)。

筛选机制      框图

can通信笔记_第6张图片

 解图

  1. 首先这个筛选器的排布是按照优先规则排布下来的,这样排是为了方便了解筛选机制
  2. 可以看到红色箭头的就是筛选器编号
  3. 优先筛选标识符列表、没有筛选出来再筛选标识符掩码模式;
  4. 例如标识符筛选到了,就把索引4消息存储在fifo邮箱中(ps邮箱属于FIFO里面,但是不清楚具体等价还是包含)(索引存储在can_RDT寄存器中)
  5. 没有匹配到的就丢弃消息;

接收处理和发送

发送大致状态原理:

  1. 为了发送消息,应用程序必须在请求发送前,通过将 CAN_TIxR 寄存器的相应 TXRQ 位置 1,选择一个空发送邮箱,并设置标识符、数据长度代码 (DLC) 和数据
  2. 邮箱立即进入挂起状 态,等待成为优先级最高的邮箱;
  3. CAN 总线变为空闲后,被安排好的邮箱中的消息即开始发送(进入发送状态);
  4. 邮箱 一旦发送成功,即恢复空状态;硬件通过将 CAN_TSR 寄存器的 RQCP 和 TXOK 位置 1,来表示发送成功。如果发送失败,失败原因将由 CAN_TSR 寄存器的 ALST 位(仲裁丢失)和/或 TERR 位(检 测到发送错误)指示。

如果需要中止值得注意的是:如果在邮箱处于发送状态时请求中止,则会出现两种结果;

1、如果邮箱发送成功,将变为空状态,同时 CAN_TSR 寄存器的 TXOK 位置 1 (也就是中止不了)

2、如果发送失败,邮箱变为已安 排状态,发送中止并变为空状态,同时 TXOK 位清零

发送优先级
按标识符
当多个发送邮箱挂起时,发送顺序由邮箱中所存储消息的标识符来确定。根据 CAN 协议的
仲裁,标识符值最低的消息具有最高的优先级。如果标识符值相等,则首先安排发送编号较
小的邮箱。
按发送请求顺序
可以通过设置 CAN_MCR 寄存器中的 TXFP 位,将发送邮箱配置为发送 FIFO。在此模式下, 
优先级顺序按照发送请求顺序来确定。该模式对分段发送非常有用。

can通信笔记_第7张图片

 TME  空标记     RQCP中止状态标记   TXOK是发送完成标记 

 TXRQ发送请求  ABRQ中止请求;  

 发送需要用到的寄存器:

  • CAN 发送邮箱标识符寄存器 (CAN_TIxR) (x=0..2)
  • CAN 邮箱数据长度控制和时间戳寄存器 (CAN_TDTxR) (x=0..2)
  • CAN 邮箱数据低位寄存器 (CAN_TDLxR) (x=0..2)
  • CAN 邮箱数据高位寄存器 (CAN_TDHxR) (x=0..2)

接收大致状态原理

can通信笔记_第8张图片

FMP  记录相应的收到的有效消息的条数的状态 

FOVR溢出标记

PS:上溢会根据配置can主寄存器的fifo溢出锁存配置,决定是否覆盖;

接受需要用到的寄存器:

  •  CAN 接收 FIFO 邮箱标识符寄存器 (CAN_RIxR) (x=0..1)
  • CAN 接收 FIFO 邮箱数据长度控制和时间戳寄存器 (CAN_RDTxR) (x=0..1)
  • CAN 接收 FIFO 邮箱数据低位寄存器 (CAN_RDLxR) (x=0..1)
  • CAN 接收 FIFO 邮箱数据高位寄存器 (CAN_RDHxR) (x=0..1)

位定时的配置公式,以及计算设计

详细位在CAN_BTR寄存器中

can通信笔记_第9张图片

因为有

 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;

附上手工计算:有点不相同但是原理一致;

can通信笔记_第10张图片

数据帧

先了解图解规则

can通信笔记_第11张图片

还有必须知道灰色是显性 等效于逻辑零;白色是隐性 等效于逻辑1;

半黑半白表示 ,可以逻辑1也可以逻辑0;

蓝色的  是可变的数据

数据帧格式:

can通信笔记_第12张图片

第一部分理解 

 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,扩展标识符;

MCU的can控制器的可以操作的数据帧的段的位:

结论:crc段 ack段 帧结束段;都是不可以主动操作的;或者说单片机是不需要这些段的,或者说这三个段完全由硬件完成(个人那么认为)

分析:

  • CAN 发送邮箱标识符寄存器 (CAN_TIxR)    
  •          CAN 邮箱数据长度控制和时间戳寄存器 (CAN_TDTxR) (x=0..2)
  •        CAN 邮箱数据低位寄存器 (CAN_TDLxR) (x=0..2)
  •       CAN 邮箱数据高位寄存器 (CAN_TDHxR) (x=0..2)

这些寄存器,可以发现以上的寄存器可以找到     数据段 id   RTR   IDE DLC ;

但是就是没有找到crc ack段 帧结束段 的相关寄存器; 

can通信笔记_第13张图片

can通信笔记_第14张图片

发送中断

为了发送效率使用发送中断处理发送长篇数据;这样可以更好的保证系统的实时性,也可以保证数据传输的实时性,和整个篇幅的完整性;

使用轮转发送:数据篇幅完整性和时效性

使用等待发送:影响真个系统的实时性

保留一个问题:怎么使用三个邮箱,让利用率提高?

开始接触的问题所在:

描述:

1、首先一个就是在开启优先级的时候,同时给优先级接收中断和发送中断一起设置;导致了无法接收中断也没有发送中断

2、发送中断:发送邮箱空中断,程序运行无法进入发送邮箱空中断;

解1:一定要一个一个配置在中断优先级,这样才能正常使用;

解2:发送中邮箱空中断,不会因为是空就能触发中断,复位初始化的时候观察发送状态寄存器TSR可以发现一开始所有发送邮箱都是空的;只有发送一次完了之后才会触发中断。所以所谓的发送邮箱空中断称之为发送完成中断;

发送中断说明:

可以通过图中看出,在使能发送邮箱空的情况下,需要CAN_TSR的RQCP0 1 2其中一个置1才能触发中断,并且完全由硬件触发;所以所谓的发送邮箱空中断,就是邮箱发送完成中断;

can通信笔记_第15张图片

所以发送长篇数据大于8 的处理方式:先发送8个字节的数据,剩下的靠发送完成中断衔接发送剩下的数据;

程序部分附上:

在     程序5: 长篇数据发送处理+接收长篇数据处理

mobus+can

不多说了利用can来做mobus协议;

附上程序6:mobus+can程序

程序

 程序1:环回模式测试

#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按下发送,然后接收到就串口打印出来;

 程序2:按键切换模式程序(在程序1基础上)


#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盒调试工具的情况下发送,之后可以看到接收的也是发送;

 程序3:添加中断

一旦监测到 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; /*返回数据帧的数据段长度*/

}






改动

 效果:没有验证中断

 程序4:大自动部分处理(在程序3的基础上改动)

一旦监测到 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();

改动:如上代码段

 效果:

程序5: 长篇数据发送处理+接收长篇数据处理

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;

mobus+can

在程序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

返回串九个字节的响应;(响应是我自己的协议,自己设置的)

你可能感兴趣的:(笔记)