STM32 CAN通信实验

详细手册

看我上传的资源 can入门教程

本章所要实现的功能是:通过 KEY_UP 键切换 CAN 通信模式,KEY1 键控制数 据发送,将切换的 CAN 模式,发送和接收的数据通过串口打印输出,整个过程 DS0 指示灯不断闪烁,提示系统正常运行。本章我们使用的是 STM32F1 的 CAN1, 程序框架如下: (1)初始化 CAN1,包括工作模式、波特率、筛选器设置等 (2)编写 CAN1

开发步骤

1)使能 CAN 时钟,将对应引脚复用映射为 CAN 功能 要使用 CAN,首先就是使能它的时钟,我们知道 CAN1 和 CAN2 是挂接在 APB1 总线上的,其发送和接收引脚对应不同的 STM32F1 IO(具体 IO 可以通过数据手 册查找,也可以在我们原理图上查找),因此使能 CAN 时钟后,还需要使能对应 端口的时钟,并且将其引脚配置为复用功能。因为我们使用的 STM32F103ZET6 芯片只有一个 CAN,即 CAN1,其对应的 IO 是 PA11(CAN1_RX)和 PA12(CAN1_TX)。 所以配置代码如下: 1. //使能相关时钟 2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能 PORTA 时 钟 3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟 4. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PA11 5. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入模式 6. GPIO_Init(GPIOA, &GPIO_InitStructure); 7. 8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PA12 9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 10. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度为 50MHz 11. GPIO_Init(GPIOA, &GPIO_InitStructure); (2)设置 CAN 工作模式、波特率等 使能了 CAN 时钟后,接下来就可以通过 CAN_MCR 寄存器配置其工作模式、 波特率大小等参数。库函数中提供了 CAN_Init()函数用来完成这一步骤,函数 原型是: uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct); 函数中第一个参数是用来设置哪个 CAN,例如 CAN1;第二个参数是一个结构 体指针变量,结构体类型是 CAN_InitTypeDef,其内包含了 CAN 工作模式及波特 率初始化的成员变量。下面我们简单介绍下它的成员: 1. typedef struct 2. { 3. uint16_t CAN_Prescaler; // 4. uint8_t CAN_Mode; // 5. uint8_t CAN_SJW; // 6. uint8_t CAN_BS1; // 7. uint8_t CAN_BS2; // 8. FunctionalState CAN_TTCM; // 9. FunctionalState CAN_ABOM; // 10. FunctionalState CAN_AWUM; // 11. FunctionalState CAN_NART; // 12. FunctionalState CAN_RFLM; // 13. FunctionalState CAN_TXFP; // 14. } CAN_InitTypeDef; CAN_Prescaler:用于设置 CAN 外设的时钟分频,它可控制时间片 tq 的时 间长度,这里设置的值最终会加 1 后再写入 BRP 寄存器位。 CAN_Mode:用于设置 CAN 的工作模式,可设置为正常模式 (CAN_Mode_Normal)、回环模式(CAN_Mode_LoopBack)、静默模式 (CAN_Mode_Silent)以及回环静默模式(CAN_Mode_Silent_LoopBack)。本实验使 用到的只有正常模式和回环模式。 CAN_SJW:用于置 SJW 的极限长度,即 CAN 重新同步时单次可增加或缩短 的最大长度,它可以被配置为 1-4tq(CAN_SJW_1/2/3/4tq)。 CAN_BS1:用于设置 CAN 位时序中的 BS1 段的长度,它可以被配置为 1-16 个 tq 长度(CAN_BS1_1/2/3…16tq)。 CAN_BS2:用于设置 CAN 位时序中的 BS2 段的长度,它可以被配置为 1-8 个 tq 长度(CAN_BS2_1/2/3…8tq)。 CAN_TTCM:用于设置是否使用时间触发功能,ENABLE 为使能,DISABLE 为失 能。时间触发功能在某些 CAN 标准中会使用到。 CAN_ABOM:用于设置是否使用自动离线管理(ENABLE/DISABLE),使用自动离 线管理可以在节点出错离线后适时自动恢复,不需要软件干预。 CAN_AWUM:用于设置是否使用自动唤醒功能(ENABLE/DISABLE),使能自动唤 醒功能后它会在监测到总线活动后自动唤醒。 CAN_NART:用于设置是否使用自动重传功能(ENABLE/DISABLE),使用自动重 传功能时,会一直发送报文直到成功为止。 CAN_RFLM:用于设置是否使用锁定接收 FIFO(ENABLE/DISABLE),锁定接收 FIFO 后,若 FIFO 溢出时会丢弃新数据,否则在 FIFO 溢出时以新数据覆盖旧 数据。 CAN_TXFP:用于设置发送报文的优先级判定方法(ENABLE/DISABLE),使能时, 以报文存入发送邮箱的先后顺序来发送,否则按照报文 ID 的优先级来发送。 了解结构体成员功能后,就可以进行配置,设置好 CAN_Prescaler、CAN_BS1 和 CAN_BS2 的值,带入到 CAN 波特率计算公式: CAN 波特率=Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler) 就可以算出波特率。本章实验我们初始化配置 CAN 为正常工作模式,波特率 为 500Kbps,配置代码如下: 1. CAN_InitTypeDef CAN_InitStructure; 2. //CAN 单元设置 3. CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式 4. CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理 5. CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过软件唤醒(清除 CAN->MCR 的 SLEEP 位) 6. CAN_InitStructure.CAN_NART=ENABLE; //使用报文自动传送 7. CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的 8. CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定 9. CAN_InitStructure.CAN_Mode= CAN_Mode_Normal; //模式设置 10. CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //重新同步跳跃宽度(Tsjw)为 tsjw+1 个时间 单位 CAN_SJW_1tq~CAN_SJW_4tq 11. CAN_InitStructure.CAN_BS1=CAN_BS1_7tq; //Tbs1 范围 CAN_BS1_1tq ~CAN_BS1_16tq 12. CAN_InitStructure.CAN_BS2=CAN_BS2_6tq;//Tbs2 范围 CAN_BS2_1tq ~ CAN_BS2_8tq 13. CAN_InitStructure.CAN_Prescaler=6; //分频系数(Fdiv)为 brp+1 14. CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN1 (3)设置 CAN 筛选器 设置好 CAN 的工作模式及波特率后,我们还需要通过 CAN_FMR 寄存器设置 CAN 筛选器,库函数中提供了 CAN_FilterInit()函数来完成这一步骤,函数原型 是: void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct); 与前面一样采用结构体方式对 CAN 筛选器进行控制管理,结构体类型是 CAN_FilterInitTypeDef。下面我们简单介绍下它的成员: 1. typedef struct 2. { 3. uint16_t CAN_FilterIdHigh; // 4. uint16_t CAN_FilterIdLow; // 5. uint16_t CAN_FilterMaskIdHigh; // 6. uint16_t CAN_FilterMaskIdLow; // 7. uint16_t CAN_FilterFIFOAssignment; // 8. uint8_t CAN_FilterNumber; // 9. uint8_t CAN_FilterMode; // 10. uint8_t CAN_FilterScale; // 11. FunctionalState CAN_FilterActivation; // 12. } CAN_FilterInitTypeDef; CAN_FilterIdHigh:用于存储要筛选的 ID,若筛选器工作在 32 位模式, 它存储的是所筛选 ID 的高 16 位;若筛选器工作在 16 位模式,它存储的就是 一个完整的要筛选的 ID。 CAN_FilterIdLow:同上一个成员一样,它也是用于存储要筛选的 ID,若筛 选器工作在 32 位模式,它存储的是所筛选 ID 的低 16 位;若筛选器工作在 16 位模式,它存储的就是一个完整的要筛选的 ID。 CAN_FilterMaskIdHigh:用于存储要筛选的 ID 或掩码。 CAN_FilterMaskIdHigh 存储的内容分两种情况,当筛选器工作在标识符列表模 式时,它的功能与 CAN_FilterIdHigh 相同,都是存储要筛选的 ID;而当筛选 器工作在掩码模式时,它存储的是 CAN_FilterIdHigh 成员对应的掩码,与 CAN_FilterIdLow 组成一组筛选器。 CAN_FilterMaskIdLow:同上一个成员一样,它也是用于存储要筛选的 ID 或掩码,只不过这里对应存储 CAN_FilterIdLow 的成员。 CAN_FilterFIFOAssignment:用于设置当报文通过筛选器的匹配后,该报文 会被存储到哪一个接收 FIFO,它的可选值为 FIFO0 或 FIFO1(CAN_Filter_FIFO0/1)。 CAN_FilterNumber:用于设置筛选器的编号,即使用的是哪个筛选器。CAN 一 共有 28 个筛选器,所以它的可输入参数范围为 0-27。 CAN_FilterMode:用于设置筛选器的工作模式,可以设置为列表模式 (CAN_FilterMode_IdList)及掩码模式(CAN_FilterMode_IdMask)。 CAN_FilterScale:用于设置筛选器的位宽,可以设置为 32 位长 (CAN_FilterScale_32bit)及 16 位长(CAN_FilterScale_16bit)。 CAN_FilterActivation:用于设置是否激活这个筛选器(ENABLE/DISABLE)。 了解结构体成员功能后,就可以进行配置,本章实验使用滤波器组 0,并工 作在 32 位标识符屏蔽位模式,配置代码如下: 1. CAN_FilterInitTypeDef CAN_FilterInitStructure; 2. //配置过滤器 3. CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器 0 4. CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 5. CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32 位 6. CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;32 位 ID 7. CAN_FilterInitStructure.CAN_FilterIdLow=0x0000; 8. CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32 位 MASK 9. CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; 10. CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器 0 关联到 FIFO0 11. CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0 12. CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化 (4)选择 CAN 中断类型,开启中断 配置好上述 3 个步骤后,CAN 就可以开始工作了,如果要开启 CAN 的接收中 断,我们还需要选择它的中断类型并使能。配置 CAN 中断类型及使能的库函数是: void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState); 第一个参数用来选择 CAN,第二个参数用来选择 CAN 中断类型,最后一个参 数用来使能或者失能 CAN 中断。 CAN 的中断类型很多,可以在 stm32f10x_can.h 文件查找,如下: 比如我们使用 FIFO0 消息挂号允许中断,那么此参数可以设置为 CAN_IT_FMP0。使能中断后还需要设置它的中断优先级,即初始化 NVIC。当产生 中断后就会进去CAN接收中断内进行处理,所以需要编写一个CAN接收中断函数, 如 CAN1_RX0_IRQHandler。本实验默认不开启 CAN 的接收中断,所以此步骤可以 不用配置。 (5)CAN 发送和接收消息 初始化 CAN 相关参数以及筛选器之后,接下来就是发送和接收消息了。库 函数中提供了发送和接受消息的函数。 发送消息的函数是: uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage); 这个函数非常好理解,第一个参数用来选择 CAN,比如 CAN1,第二个参数是 一个结构体指针变量,结构体类型是 CanTxMsg,其内包含了往邮箱发送的报文 信息。下面我们简单介绍下它的成员: 1. typedef struct 2. { 3. uint32_t StdId; 4. uint32_t ExtId; 5. uint8_t IDE; 6. uint8_t RTR; 7. uint8_t DLC; 8. uint8_t Data[8]; 9. } CanTxMsg; StdId:用于存储报文的 11 位标准标识符,范围是 0-0x7FF。 ExtId:用于存储报文的 29 位扩展标识符,范围是 0-0x1FFFFFFF。ExtId 与 StdId 这两个成员哪一个有效要根据下面的 IDE 位配置。 IDE:用于存储扩展标志 IDE 位的值,其值可配置为 CAN_ID_STD 和 CAN_ID_EXT。如果为 CAN_ID_STD 时表示本报文是标准帧,使用 StdId 成员存 储报文 ID。如果为 CAN_ID_EXT 时表示本报文是扩展帧,使用 ExtId 成员存储 报文 ID。 RTR:用于存储报文类型标志 RTR 位的值,当它的值为宏 CAN_RTR_Data 时 表示本报文是数据帧;当它的值为宏 CAN_RTR_Remote 时表示本报文是遥控帧, 由于遥控帧没有数据段,所以当报文是遥控帧时,下面的 Data[8]成员的内容是 无效的。 DLC:用于存储数据帧数据段的长度,其值范围是 0-8,当报文是遥控帧时 DLC 值为 0。 Data[8]:用于存储数据帧中数据段的数据。 当我们需要发送报文时,就需要对此结构体进行初始化,然后调用该函数发 送出去,例如: 1. CanTxMsg TxMessage; 2. TxMessage.StdId=0x12; // 标准标识符为 0 3. TxMessage.ExtId=0x12; // 设置扩展标示符(29 位) 4. TxMessage.IDE=0; // 使用扩展标识符 5. TxMessage.RTR=0; // 消息类型为数据帧,一帧 8 位 6. TxMessage.DLC=8; // 发送两帧信息 7. for(i=0;i<8;i++) 8. TxMessage.Data[8]=msg[i]; // 第一帧信息 9. CAN_Transmit(CAN1, &TxMessage); 接收消息的函数是: void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage); 前两个参数很好理解,选择 CAN 和 FIFO。第三个参数是一个结构体指针变 量,结构体类型是 CanRxMsg,与 CanTxMsg 比较类似,其内包含了从邮箱接收的 报文信息。下面我们简单介绍下它的成员: 1. typedef struct 2. { 3. uint32_t StdId; 4. uint32_t ExtId 5. uint8_t IDE; 6. uint8_t RTR; 7. uint8_t DLC; 8. uint8_t Data[8]; 9. uint8_t FMI; 10. } CanRxMsg; (6)CAN 状态获取 当使用 CAN 进行数据传输时,我们会通过获取 CAN 状态标志来确认是否传输 完成,比如说在接收报文时,通过检测标志位获知接收 FIFO 的状态,若收到报 文,可调用库函数 CAN_Receive 把接收 FIFO 中的内容读取到预先定义的接收 类型结构体中,然后再访问该结构体即可利用报文了。 库函数中提供了很多获取 CAN 状态标志的函数,如 CAN_TransmitStatus() 函数, CAN_MessagePending()函数, CAN_GetFlagStatus()函数等等,大家可 以根据需要来调用。 将以上几步全部配置好后,我们就可以使用 STM32F1 的 CAN 收发数据了。

can.h+can.c

#ifndef _can_H
#define _can_H

#include "system.h"
                                

#define CAN_RX0_INT_ENABLE 0   //不使用中断

void CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode);//CAN初始化
 
u8 CAN_Send_Msg(u8* msg,u8 len);                        //发送数据

u8 CAN_Receive_Msg(u8 *buf);                            //接收数据


#endif
#include "can.h"
#include "usart.h"

//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元.   范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元.   范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024;  tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+tbs2+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化OK;
//    其他,初始化失败;
void CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    CAN_InitTypeDef        CAN_InitStructure;
    CAN_FilterInitTypeDef  CAN_FilterInitStructure;
    
#if CAN_RX0_INT_ENABLE 
    NVIC_InitTypeDef          NVIC_InitStructure;
#endif
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); //打开CAN1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //PA端口时钟打开
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;        //PA11       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;      //上拉输入模式
    GPIO_Init(GPIOA, &GPIO_InitStructure);    

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;        //PA12       
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //IO口速度为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //CAN单元设置
       CAN_InitStructure.CAN_TTCM=DISABLE;    //非时间触发通信模式   
      CAN_InitStructure.CAN_ABOM=DISABLE;    //软件自动离线管理      
      CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
      CAN_InitStructure.CAN_NART=ENABLE;    //使用报文自动传送 
      CAN_InitStructure.CAN_RFLM=DISABLE;    //报文不锁定,新的覆盖旧的  
      CAN_InitStructure.CAN_TXFP=DISABLE;    //优先级由报文标识符决定 
      CAN_InitStructure.CAN_Mode= mode;     //模式设置 
      CAN_InitStructure.CAN_SJW=tsjw;    //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
      CAN_InitStructure.CAN_BS1=tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
      CAN_InitStructure.CAN_BS2=tbs2;//Tbs2范围CAN_BS2_1tq ~    CAN_BS2_8tq
      CAN_InitStructure.CAN_Prescaler=brp;  //分频系数(Fdiv)为brp+1    
      CAN_Init(CAN1, &CAN_InitStructure);   // 初始化CAN1
    
    //配置过滤器
       CAN_FilterInitStructure.CAN_FilterNumber=0;      //过滤器0
      CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; 
      CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位 
      CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;32位ID
      CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
      CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
      CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;//掩码设置都为0,没有ID限制
       CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
      CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器0
      CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化
    
#if CAN_RX0_INT_ENABLE 
    CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);                //FIFO0消息挂号中断允许.            

    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // 主优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // 次优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
#endif
}

#if CAN_RX0_INT_ENABLE    //使能RX0中断
//中断服务函数    中断接收            
void USB_LP_CAN1_RX0_IRQHandler(void)
{
      CanRxMsg RxMessage;
    int i=0;
    CAN_Receive(CAN1, 0, &RxMessage);
    for(i=0;i<8;i++)
    printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif

//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)    
//len:数据长度(最大为8)                     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//         其他,失败;
u8 CAN_Send_Msg(u8* msg,u8 len)
{    
    u8 mbox;
    u16 i=0;
    CanTxMsg TxMessage;
    TxMessage.StdId=0x12;     // 标准标识符为0
    TxMessage.ExtId=0x12;     // 设置扩展标示符(29位)
    TxMessage.IDE=0;          // 使用标准标识符
    TxMessage.RTR=0;          // 消息类型为数据帧,一帧8位
    TxMessage.DLC=len;                             // 发送两帧信息
    for(i=0;i<len;i++)
        TxMessage.Data[i]=msg[i];                 // 第一帧信息          
    mbox= CAN_Transmit(CAN1, &TxMessage);   
    i=0;
    while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;    //等待发送结束
    if(i>=0XFFF)return 1;
    return 0;        
}

//can口接收数据查询
//buf:数据缓存区;     
//返回值:0,无数据被收到;
//         其他,接收的数据长度;
u8 CAN_Receive_Msg(u8 *buf)
{                      
     u32 i;
    CanRxMsg RxMessage;
  if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;        //没有接收到数据,直接退出 
  CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据    
  for(i=0;i<RxMessage.DLC;i++)
        buf[i]=RxMessage.Data[i];  
    return RxMessage.DLC;    
}

main.c

#include "stm32f10x.h"
#include "led.h"
#include "system.h"
#include "SysTick.h"
#include "beep.h"
#include "key.h"
#include "exti.h"
#include "time.h"
#include "pwm.h"
#include "usart.h"
#include "stdio.h"
#include "iwdg.h"
#include "wwdg.h"
#include "input.h"
#include "touch_key.h"
#include "wkup.h"
#include "adc.h"
#include "adc_temp.h"
#include "lsens.h"
#include "dac.h"
#include "dma.h"
#include "rtc.h"
#include "at24cxx.h"
#include "i2c.h"
#include "ds18b20.h"
#include "hwjs.h"
#include "rs485.h"
#include "can.h"


int main()
{
    u8 i=0,j=0;
    u8 key;
    u8 mode=0;
    u8 res;
    u8 tbuf[8];
    u8 rbuf[8];
    
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
    LED_Init();
    USART1_Init(115200);
    KEY_Init();
    CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_Normal);//500Kbps波特率
    //另一个设备也必须为500kbps,不然无法传送信息
    while(1)
    {
        key=KEY_Scan(0);
        if(key==KEY_UP_PRESS)  //模式切换
        {
            mode=!mode;
            CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,mode);
            if(mode==0)
            {
                printf("正常模式\r\n");
            }
            else
            {
                printf("回环模式\r\n");
            }
        }
        if(key==KEY1_PRESS)  //发送数据
        {
            for(j=0;j<8;j++)
            {
                tbuf[j]=j;
            }
            res=CAN_Send_Msg(tbuf,8);
            if(res)
            {
                printf("发送失败!\r\n");
            }
            else
            {
                printf("发送数据:");
                for(j=0;j<8;j++)
                {
                    printf("%X  ",tbuf[j]);//16进制格式输出
                }
                printf("\r\n");
            }
        }
        res=CAN_Receive_Msg(rbuf);
        if(res)
        {    
            printf("接收数据:");
            for(j=0;j<8;j++)
            {
                printf("%X  ",rbuf[j]);
            }
            printf("\r\n");
        }
        
        i++;
        if(i%20==0)
        {
            LED1=!LED1;
        }
        delay_ms(10);
    }
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值