打印
[STM32F1]

ST分享大集结+CAN 通信

[复制链接]
2791|26
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dingbo95|  楼主 | 2017-11-24 12:07 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
CAN  简介
CAN 是一种 ISO 国际标准化的串行通信协议,是 1986 年德国电气商博世公司开发出面向汽车开发出来的,此后,CAN 通过 ISO11898 及 ISO11519 进行了标准化,现在在欧洲已是汽车网络的标准协议。 现在,CAN 的高性能和可靠性已被认同,并被广泛地应用于工业自化船舶、医疗设备、工业设备等方面。CAN 传输数据只需要两根线就可以了。CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。



沙发
dingbo95|  楼主 | 2017-11-24 12:10 | 只看该作者
CAN 协议特点
1. 多主控制。
也就是在总线空闲的时候,所有的单元都可以发送消息,而两个或者两个以上的单元同时发送消息的时候呢,它是根据标示符(也就是 ID)来决定优先级的。所以这里的 ID 并不是地址,而是表示访问总线的消息的优先级。
2. 系统的柔软性
与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。不
像 I2C 总线上面每个单元都有自己的地址。
3. 通信速度
根据整个网络的规模,可设定适合的通信速度。 在同一网络中,所有单元必须设定成统一的通信速度。而且传输很快。最高 1Mbps(距离小于40M),最远可达 10KM(速率低于 5Kbps)
4. 远程数据请求
可通过发送“遥控帧” 请求其他单元发送数据。
5. 错误检测功能·错误通知功能·错误恢复功能
测出错误的单元会立即同时通知其他所有单元(错误通知功能)。 正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。
6. 故障封闭
CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。
7. 连接
CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负
载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。

使用特权

评论回复
板凳
dingbo95|  楼主 | 2017-11-24 12:11 | 只看该作者
CAN  模块原理图

01.png (90.49 KB )

01.png

使用特权

评论回复
地板
dingbo95|  楼主 | 2017-11-24 12:13 | 只看该作者
CAN操作一般步骤
1. 打开时钟。
我们要使用的 CAN1 所以我们讲 CAN1 的时钟,还有要使用的 IO 口的时钟。在这里我们看到 CAN 还有编号!其实呢,在互联型STM32 中是有两个 CAN 的,一个主 CAN 一个从 CAN,不过我们开发板上面使用的是增强型不是互联型,所以只有一个 CAN。所以设置代为:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
2. 配置 IO 的模式
我们使用的收发 IO 口是 PA11 和 PA12。在这里我们要把 PA11 设置为上拉 输 入 : GPIO_Mode_IPD , PA12 设 置 为 复 用 推 挽 输出 :
GPIO_Mode_AF_PP。代码为:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PA12
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PA11
GPIO_Init(GPIOA, &GPIO_InitStructure);
3. 复位 CAN 外设及结构体函数。
我们使用的初始化的结构体成员很多,有时候有些成员不用设置,所以我们复位初始化一下。代码为:
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);

使用特权

评论回复
5
dingbo95|  楼主 | 2017-11-24 12:15 | 只看该作者
4. 初始化 CAN
初始化 CAN 我们调用 CAN_Init()这个函数,这个函数跟其他的初始化函数一样,有两个参数。第一个参数用来选择你要初始化的CAN,我们初始化CAN1所以设置为:CAN1。第二个参数是传递一个结构体指针。这个结构体里面包含了各种 CAN的设置,对于理解 CAN 通非常重要,接下来我们就来详细讲解一下这个结构体的各个成员所代表的意义。
1) CAN_TTCM:表示是否使用时间触发模式。那什么是时间触发模式呢?时间触发通信模式下,CAN 硬件的内部定时器被激活,并且被
用于产生时间戳,分别存储在 CAN_RDTxR/CAN_TDTxR 寄存器中。内部定时器在接收和发送的帧起始位的采样点位置被采样,并生成间戳,禁止自动重传模式,主要用于满足 CAN 标准中时间触发通信选项的要求。在该模式下,发送操作只会执行一次。如果发送操作失败了,不管是由于仲裁丢失或出错,硬件都不会再自动发送该报文。在一次发送操作结束后,硬件认为发送请求已经完成,从而对CAN_TSR寄存器的 RQCP 位置 1,同时发送的结果反映在CAN_TSR 寄存器的 TXOK、ALST 和 TERR 位上。我们这里不使用时间触发模式,所以我们设置为:DISABLE2) CAN_ABOM:表示是否使用自动离线管理。什么是自动离线管理呢?其实设置当 CAN 处在离线状态下时,是否能够离开离线状态。当它使能的时候,单片机工作的过程是:当硬件检测到 128 次 11 位连续的隐形位,就自动退出离线状态;当它失能的时候,需要用软件来设置 CAN_MCR 寄存器中的 INRQ,然后当硬件检测到 128 次11 位连续的隐形位,就退出离线状态。现在我们知道自动离线管理是怎么回事儿了,但是呢,我们又有点疑问,离线状态是怎么事儿?这个呢,它是当 TEC 大于 255 时,BxCAN 就进入离线状态,那 TEC又是什么呢?TEC 呢其实是一个 9 位的发送错误计数器的低 8 位,在 CAN_ESR 寄存器里面,然后它根据 CAN 标准,接收出错时,根据出错的条件,加 1 或者加 8。(详细的错误标准大家可以查看 CAN手册)现在我们恍然大悟了吧,其实离线状态是 CAN 出错了之后CAN 进入的状态。 (所以我们说 CAN 有故障隔离机制就是这么回事)我们这里不使用,那么设置为:DISABLE。
3) CAN_AWUM:表示是否使用睡眠模式自动唤醒。在 CAN 正常工作的时候有三种模式,一种是初始化模式,一种是正常模式和一种睡
眠模式。只有在正常模式的时候,CAN 才能正常收发报文。一般睡眠模式是工作在低功耗下休眠的。我们这里不使用,所以设置为:
DISABLE。
4) CAN_NART:表示是否使用非自动重传输。按照 CAN 标准,CAN硬件在发送报文失败时会一直自动重传直到发送成功。(在时间模式
下不使用。)我们这里要使用自动重传输,设置为:DISABLE。
5) CAN_RFLM:表示是否使用 FIFO 锁定模式。这个 FIFO 锁定模式的意义在于,FIFO 未被锁定时,接收溢出时,当接收 FIFO 的报文未
被读出,下一个收到的报文会覆盖原有的报文;FIFO 被锁定时,接收溢出时,当接收 FIFO 的报文未被读出,下一个收到的报文会被丢
弃。我们这里不锁定,也就是新数据会覆盖旧数据,所以设置为:DISABLE。

使用特权

评论回复
6
dingbo95|  楼主 | 2017-11-24 12:17 | 只看该作者
CAN 初始化函数
void CAN1_Config(uint8_t mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
/* 初始化 IO 口 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PA12
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PA11
GPIO_Init(GPIOA, &GPIO_InitStructure);
/****************************************************************/
/****************** CAN 设置和初始化 ****************************/
/****************************************************************/
/* 打开时钟使能 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
/* 初始化 CAN 的参数 */
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
/* CAN 参数初始化 */
CAN_InitStructure.CAN_TTCM = DISABLE; //失能时间触发模式
CAN_InitStructure.CAN_ABOM = DISABLE; //失能自动离线管理
CAN_InitStructure.CAN_AWUM = DISABLE; //失能睡眠模式通过软
件唤醒
CAN_InitStructure.CAN_NART = DISABLE; //失能非自动重传输模
式(也就是会自动重传输)
CAN_InitStructure.CAN_RFLM = DISABLE; //失能接收 FIFO 锁定模
式,新数据会覆盖旧数据
CAN_InitStructure.CAN_TXFP = DISABLE; //优先级由报文标识符决

CAN_InitStructure.CAN_Mode = mode; //有普通模式和拓展模式
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; //重新同步跳跃宽度 1
个时间单位
/* 波特率设置, 当 APB1 的时钟频率是 36MHZ 的时候。 波特率的公式
为: */
/* 波 特 率 (Kpbs) = 36M / ((CAN_BS1 + CAN_BS2 + 1) *
CAN_Prescaler) */
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; //时间段 1 为 8 个时间
单位
CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; //时间段 2 为 7 个时间
单位
CAN_InitStructure.CAN_Prescaler = 5;
CAN_Init(CAN1, &CAN_InitStructure);
#ifdef CAN_RX0_INT_ENABLE
CAN1_NVIC_Config();
#endif
}

使用特权

评论回复
7
yklstudent| | 2017-11-24 13:28 | 只看该作者
楼主试过STM32F0的CAN是否可以跟STM32F1或者STM32F4的CAN进行通信?

使用特权

评论回复
8
一路向北lm| | 2017-11-24 22:10 | 只看该作者
感谢分享,资料不错。

使用特权

评论回复
9
yongwong9901| | 2017-11-25 11:23 | 只看该作者
mark!

使用特权

评论回复
10
CCompton| | 2017-11-28 11:09 | 只看该作者
CAN 有故障隔离机制

使用特权

评论回复
11
dongnanxibei| | 2017-11-28 11:37 | 只看该作者
最近几年新出的ARM内核单片机,好多都是带CAN的,这个以后会越来越多。

使用特权

评论回复
12
dingbo95|  楼主 | 2017-11-28 12:16 | 只看该作者
dongnanxibei 发表于 2017-11-28 11:37
最近几年新出的ARM内核单片机,好多都是带CAN的,这个以后会越来越多。

CAN是比较实用的

使用特权

评论回复
13
dingbo95|  楼主 | 2017-11-28 12:17 | 只看该作者
CCompton 发表于 2017-11-28 11:09
CAN 有故障隔离机制

这个还是很难克服的

使用特权

评论回复
14
dingbo95|  楼主 | 2017-11-28 12:18 | 只看该作者

有错误欢迎批评指正,

使用特权

评论回复
15
dingbo95|  楼主 | 2017-11-28 12:18 | 只看该作者
yklstudent 发表于 2017-11-24 13:28
楼主试过STM32F0的CAN是否可以跟STM32F1或者STM32F4的CAN进行通信?

目前只用F1其它的少点。

使用特权

评论回复
16
dingbo95|  楼主 | 2017-11-28 12:20 | 只看该作者
下面介绍下can驱动(回环模式)
第一步CAN的GPIO 配置
static void CAN_GPIO_Config(void)
{
         GPIO_InitTypeDef GPIO_InitStructure;           

  /* Enable GPIO clock */
  RCC_APB2PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);
        
        //重映射引脚
        GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);

         /* Configure CAN TX pins */
  GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                         // 复用推挽输出
  GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);
        
        /* Configure CAN RX  pins */
  GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                     // 上拉输入
  GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);

        
}

使用特权

评论回复
17
dingbo95|  楼主 | 2017-11-28 12:21 | 只看该作者
接着就是CAN的NVIC 配置,第1优先级组,0,0优先级
static void CAN_NVIC_Config(void)
{
           NVIC_InitTypeDef NVIC_InitStructure;
                /* Configure one bit for preemption priority */
                NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
                 /*中断设置*/
                NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ;           //CAN1 RX0中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;                   //抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                           //子优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

使用特权

评论回复
18
dingbo95|  楼主 | 2017-11-28 22:16 | 只看该作者
CAN的模式 配置
static void CAN_Mode_Config(void)
{
        CAN_InitTypeDef        CAN_InitStructure;
        /************************CAN通信参数设置**********************************/
        /* Enable CAN clock */
  RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

        /*CAN寄存器初始化*/
        CAN_DeInit(CANx);
        CAN_StructInit(&CAN_InitStructure);

        /*CAN单元初始化*/
        CAN_InitStructure.CAN_TTCM=DISABLE;                           //MCR-TTCM  关闭时间触发通信模式使能
        CAN_InitStructure.CAN_ABOM=ENABLE;                           //MCR-ABOM  自动离线管理
        CAN_InitStructure.CAN_AWUM=ENABLE;                           //MCR-AWUM  使用自动唤醒模式
        CAN_InitStructure.CAN_NART=DISABLE;                           //MCR-NART  禁止报文自动重传          DISABLE-自动重传
        CAN_InitStructure.CAN_RFLM=DISABLE;                           //MCR-RFLM  接收FIFO 锁定模式  DISABLE-溢出时新报文会覆盖原有报文  
        CAN_InitStructure.CAN_TXFP=DISABLE;                           //MCR-TXFP  发送FIFO优先级 DISABLE-优先级取决于报文标示符
        CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;  //回环工作模式
        CAN_InitStructure.CAN_SJW=CAN_SJW_2tq;                   //BTR-SJW 重新同步跳跃宽度 2个时间单元
         
        /* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) 波特率即为时钟周期tq*(1+3+5)  */
        CAN_InitStructure.CAN_BS1=CAN_BS1_5tq;                   //BTR-TS1 时间段1 占用了5个时间单元
        CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;                   //BTR-TS1 时间段2 占用了3个时间单元       
       
        /* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB1 = 36 MHz) */
        CAN_InitStructure.CAN_Prescaler =4;                   ////BTR-BRP 波特率分频器  定义了时间单元的时间长度 36/(1+5+3)/4=1 Mbps
        CAN_Init(CANx, &CAN_InitStructure);
}

使用特权

评论回复
19
dingbo95|  楼主 | 2017-11-28 22:17 | 只看该作者
CAN的过滤器 配置
static void CAN_Filter_Config(void)
{
        CAN_FilterInitTypeDef  CAN_FilterInitStructure;

        /*CAN筛选器初始化*/
        CAN_FilterInitStructure.CAN_FilterNumber=0;                                                //筛选器组0
        CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;        //工作在掩码模式
        CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;        //筛选器位宽为单个32位。
        /* 使能筛选器,按照标志的内容进行比对筛选,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */

        CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;                //要筛选的ID高位
        CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要筛选的ID低位
        CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;                        //筛选器高16位每位必须匹配
        CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;                        //筛选器低16位每位必须匹配
        CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;                                //筛选器被关联到FIFO0
        CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;                        //使能筛选器
        CAN_FilterInit(&CAN_FilterInitStructure);
        /*CAN通信中断使能*/
        CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);
}

使用特权

评论回复
20
dingbo95|  楼主 | 2017-11-28 22:17 | 只看该作者
完整配置CAN的功能
void CAN_Config(void)
{
  CAN_GPIO_Config();
  CAN_NVIC_Config();
  CAN_Mode_Config();
  CAN_Filter_Config();   
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

52

主题

1197

帖子

5

粉丝