首页 > 其他分享 >STM32高级:CAN通讯案例1:环回静默模式测试 (寄存器代码)(详解)

STM32高级:CAN通讯案例1:环回静默模式测试 (寄存器代码)(详解)

时间:2024-12-24 16:27:06浏览次数:9  
标签:初始化 BTR 报文 寄存器 STM32 环回 CAN1 data MCR

目录

需求描述

思路:

初始化函数

GPIO引脚模块

1        RCC

2        AFIO

3        GPIO

CAN模块

1        MCR和MSR

2        MCR

发送报文

1        TSR

2        数据帧的书写(邮箱寄存器)

1        TIxR(TIR)

3        TDTxR(TDTR)

4        TDLxR/TDHxR(TDLR/TDHR)

3        TIR

6        TSR

读取报文

接收过滤器配置

can.h

can.c

main.c


需求描述

我们使用环回静默模式测试CAN能否正常工作。把接收到的报文数据发送到串口输出,看是否可以正常工作。

思路:

首先书写函数,初始化,发送报文,接收报文以及过滤器

初始化函数

初始化函数大概分为:GPIO引脚模块和CAN模块

GPIO引脚模块

1        RCC

开时钟

2        AFIO

查看原理图发现stm32can控制器对接收器也就是can的外置芯片,连接的引脚是PB8和PB9,默认是在PA11和PA12上,所以要对这两个引脚进行重定向。

3        GPIO

配置工作模式,

PB8 rx 浮空输入 mode 00 cnf 01PB9 tx 复用推挽输出 mode 11 cnf 10

CAN模块

1        MCR和MSR

看需求是环回静默模式通过手册,配置测试模式中的循环静默模式需要先配置初始化模式。

 

要配置初始化模式可以看到进入是两步置1和等待,退出是清0和等待,需要MCR和MSR

2        MCR

看到初始化要至少涉及两个寄存器MCR

通过查看可以知道MCR单独配置要位6和位5以及位1(退出睡眠)

3        BTR

在手册中测试模式下环会静默模式通过BTR,更加印证了初始化模式所设计到的BTR。

SILM和LBKM置1。 

4        BTR

BRP:确定分频器:我们要设置一个时间单元1微秒,CAN是在APB1上所以是36MHz,推出我们要设置36分频,寄存器要加1,所以这里配置35。

TS1和TS2:配置相位缓冲段的时间单元,首先CAN将每位分为4个段,但是在STM32中,传播时间段和相位缓冲段1合并,总共我们要配置10个时间单元,这里在寄存器中,将相位缓冲段1和相位缓冲段2配置位2和5(因为这里+1了)。

配置同步跳跃宽度为1(+1特性)。

发送报文

CAN的发送和接收是由邮箱来控制的,根据手册中的发送处理

1        TSR

通过等待TME0位是否等于0,进行等待发送。

2        数据帧的书写(邮箱寄存器)

五个部分:ID,RTR(什么帧),IDE(格式),DLC,DATA,包装成函数参数只有三个ID、DLC(长度)、DATA。

1        TIxR(TIR)

ID和两个设置写入TIR中,记得先清0。

3        TDTxR(TDTR)

DLC写入到TDTR中,记得先清0

4        TDLxR/TDHxR(TDLR/TDHR)

将发送的数据写在TDLR(低4字节)TDHR(高4字节)中。

3        TIR

TXRQ位,请求发送数据。

6        TSR

TXOK0位等待发送完成。

读取报文

首先需要定义一个结构体,结构体里由ID、数据以及长度

根据手册可以看到使用了RFR寄存器

 1        RF0R

通过FMP0位产看报文数量

2        RIR

读取报文ID

3        RDTR

读取数据长度

4        RDLR/RDHR

读取数据

5        RF0R

通过RF0M0 释放,再读取下一个报文

接收过滤器配置

1        FMR

FINIT:进入初始化

2        FM1R

FBMx:过滤器模式:0标识符屏蔽模式,1标识符列表模式

3        FS1R

FSCx:过滤器位宽设置:

4        FFA1R

FFA0:过滤完要进入哪个接收邮箱

5        FR1

保存ID的寄存器

6        FR2

保存掩码的寄存器

7        FA1R

FACT0:表示激活0的过滤器,相当于使能位

8        FMR

FINIT:退出初始化

can.h

#ifndef __CAN_H
#define __CAN_H

#include "stm32f10x.h"

// 定义结构体,保存接收到的报文信息
typedef struct
{
    uint16_t stdID;
    uint8_t data[8];
    uint8_t len;
} RxMsg;

// 初始化
void CAN_Init(void);

// 发送报文
void CAN_SendMsg(uint16_t stdID, uint8_t * data, uint8_t len);

// 接收报文
void CAN_ReceiveMsg(RxMsg rxMsg[], uint8_t * msgCount);

#endif

can.c

#include "can.h"

static void CAN_FilterConfig(void);

// 初始化
void CAN_Init(void)
{
    // 1. 开启时钟:CAN、GPIO、AFIO
    RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;

    // 2. 重映射PB8、PB9
    AFIO->MAPR |= AFIO_MAPR_CAN_REMAP_1;
    AFIO->MAPR &= ~AFIO_MAPR_CAN_REMAP_0;

    // 3. GPIO工作模式定义:
    // PB8 - 浮空输入 MODE 00,CNF 01
    GPIOB->CRH &= ~GPIO_CRH_MODE8;
    GPIOB->CRH &= ~GPIO_CRH_CNF8_1;
    GPIOB->CRH |= GPIO_CRH_CNF8_0;
    
    // PB9 - 复用推挽输出, MODE 11,CNF 10
    GPIOB->CRH |= GPIO_CRH_MODE9;
    GPIOB->CRH |= GPIO_CRH_CNF9_1;
    GPIOB->CRH &= ~GPIO_CRH_CNF9_0;

    // 4. CAN 初始化基本配置
    // 4.1 进入初始化模式
    CAN1->MCR |= CAN_MCR_INRQ;
    while ((CAN1->MSR & CAN_MSR_INAK) == 0)
    {}

    // 4.2 退出睡眠模式
    CAN1->MCR &= ~CAN_MCR_SLEEP;
    while ((CAN1->MSR & CAN_MSR_SLAK) != 0)
    {}

    // 4.3 自动离线管理
    CAN1->MCR |= CAN_MCR_ABOM;

    // 4.4 自动唤醒管理
    CAN1->MCR |= CAN_MCR_AWUM;

    // 4.5 配置环回静默测试模式
    CAN1->BTR |= CAN_BTR_SILM;
    CAN1->BTR |= CAN_BTR_LBKM;

    // 4.6 配置位时序
    // 4.6.1 配置波特率分频器,36分频,Tq = 1us
    CAN1->BTR &= ~CAN_BTR_BRP;
    CAN1->BTR |= (35 << 0);

    // 4.6.2 配置BS1和BS2的时间长度
    CAN1->BTR &= ~CAN_BTR_TS1;
    CAN1->BTR |= (2 << 16);

    CAN1->BTR &= ~CAN_BTR_TS2;
    CAN1->BTR |= (5 << 20);

    // 4.6.3 再同步跳跃宽度
    CAN1->BTR &= ~CAN_BTR_SJW;
    CAN1->BTR |= (1 << 24);

    // 4.7 退出初始化模式
    CAN1->MCR &= ~CAN_MCR_INRQ;
    while ((CAN1->MSR & CAN_MSR_INAK) != 0)
    {}

    // 5. CAN过滤器配置
    CAN_FilterConfig();
}

// 定义静态函数,进行过滤器配置
static void CAN_FilterConfig(void)
{
    // 1. 进入初始化模式
    CAN1->FMR |= CAN_FMR_FINIT;

    // 2. 配置过滤器工作模式:0 - 屏蔽位模式
    CAN1->FM1R &= ~CAN_FM1R_FBM0;

    // 3. 配置位宽:1 - 32位
    CAN1->FS1R |= CAN_FS1R_FSC0;

    // 4. 设置关联的FIFO:FIFO0
    CAN1->FFA1R &= ~CAN_FFA1R_FFA0;

    // 5. 设置过滤器组0的ID寄存器:FR1
    CAN1->sFilterRegister[0].FR1 = 0x06e << 21;

    // 6. 设置过滤器组0的掩码寄存器:FR2 = 0,不做过滤,全部接收
    CAN1->sFilterRegister[0].FR2 = 0x7f1 << 21;

    // 7. 激活过滤器组0
    CAN1->FA1R |= CAN_FA1R_FACT0;

    // 8. 退出初始化模式
    CAN1->FMR &= ~CAN_FMR_FINIT;
}

// 发送报文:标准ID、数据、长度(使用发送邮箱0)
void CAN_SendMsg(uint16_t stdID, uint8_t * data, uint8_t len)
{
    // 1. 等待发送邮箱0为空
    while ( (CAN1->TSR & CAN_TSR_TME0) == 0)
    {
    }

    // 2. 包装要发送的数据帧
    // 2.1 设置标准ID
    CAN1->sTxMailBox[0].TIR &= ~CAN_TI0R_STID;
    CAN1->sTxMailBox[0].TIR |= stdID << 21;

    // 2.2 设置为标准帧
    CAN1->sTxMailBox[0].TIR &= ~CAN_TI0R_IDE;

    // 2.3 设置为数据帧
    CAN1->sTxMailBox[0].TIR &= ~CAN_TI0R_RTR;

    // 2.4 设置数据长度
    CAN1->sTxMailBox[0].TDTR &= ~CAN_TDT0R_DLC;
    CAN1->sTxMailBox[0].TDTR |= len << 0;

    // 2.5 设置数据
    // 先清零寄存器
    CAN1->sTxMailBox[0].TDLR = 0;
    CAN1->sTxMailBox[0].TDHR = 0;

    // 循环处理每一个字节数据
    for (uint8_t i = 0; i < len; i++)
    {
        // 判断前4个字节,放到TDLR中
        if (i < 4)
        {
            CAN1->sTxMailBox[0].TDLR |= data[i] << (i * 8);
        } 
        // 后4个字节,放到TDHR中
        else
        {
            CAN1->sTxMailBox[0].TDHR |= data[i] << ((i - 4) * 8);
        }
    }

    // 3. 请求发送数据帧
    CAN1->sTxMailBox[0].TIR |= CAN_TI0R_TXRQ;

    // 4. 等待发送完成
    while ((CAN1->TSR & CAN_TSR_TXOK0) == 0)
    {
    }
}

// 接收报文:结构体数组,数组长度(从FIFO0中读取)
void CAN_ReceiveMsg(RxMsg rxMsg[], uint8_t * msgCount)
{
    // 1. 获取FIFO0的报文个数,通过指针返回
    * msgCount = (CAN1->RF0R & CAN_RF0R_FMP0) >> 0;

    // 2. 循环读取每一个报文
    for (uint8_t i = 0; i < *msgCount; i++)
    {
        // 定义指针,指向当前保存报文的数据对象
        RxMsg * msg = &rxMsg[i];

        // 2.1 读取ID
        msg->stdID = (CAN1->sFIFOMailBox[0].RIR >> 21) & 0x7ff;

        // 2.2 读取数据长度
        msg->len = (CAN1->sFIFOMailBox[0].RDTR >> 0) & 0x0f;
        
        // 2.3 读取数据
        uint32_t low = CAN1->sFIFOMailBox[0].RDLR;
        uint32_t high = CAN1->sFIFOMailBox[0].RDHR;

        for (uint8_t j = 0; j < msg->len; j++)
        {
            // 如果是前4个字节,就从RDLR中提取
            if (j < 4)
            {
                msg->data[j] = (low >> (8 * j)) & 0xff;
            }      
            // 如果是后4个字节,就从RDHR中提取
            else
            {
                msg->data[j] = (high >> (8 * (j - 4))) & 0xff;
            }
        }

        // 2.4 释放FIFO0,出队,再读取下一个报文
        CAN1->RF0R |= CAN_RF0R_RFOM0;
    }
}

main.c

#include "usart.h"
#include "can.h"
#include <string.h>

int main(void)
{
	// 初始化
	USART_Init();
	CAN_Init();

	printf("尚硅谷CAN通讯实验:环回静默模式测试,寄存器版...\n");

	// 1. 发送三个报文
	uint16_t stdID = 0x066;
	uint8_t * data = "abcdefg";
	CAN_SendMsg(stdID, data, strlen((char *)data));

	stdID = 0x068;
	data = "123";
	CAN_SendMsg(stdID, data, strlen((char *)data));

	stdID = 0x067;
	data = "xyz";
	CAN_SendMsg(stdID, data, strlen((char *)data));

	// 2. 接收数据
	RxMsg rxMsg[3];
	uint8_t msgCount;

	CAN_ReceiveMsg(rxMsg, &msgCount);

	printf("报文接收完毕!count = %d\n", msgCount);

	// 3. 打印输出报文
	for (uint8_t i = 0; i < msgCount; i++)
	{
		printf("stdID = %#X, len = %d, data = %.*s\n",
			 rxMsg[i].stdID, rxMsg[i].len, rxMsg[i].len, rxMsg[i].data);
	}

	while (1)
	{
	}
}

标签:初始化,BTR,报文,寄存器,STM32,环回,CAN1,data,MCR
From: https://blog.csdn.net/qq_64219867/article/details/144673719

相关文章

  • STM32高级物联网通信之以太网通讯
    目录以太网通讯基础知识什么是以太网互联网和以太网的区别1)概念与范围(1)互联网(2)以太网2)技术特点(1)互联网(2)以太网3)应用场景(1)互联网(2)以太网以太网的层次1)物理层2)数据链路层OSI7层模型TCPIP4层模型一些常见的网络协议1)IP协议2)TCP协议3)UDP协议4)HTTP和HTTPS协......
  • STM32CubeMX卸载和安装教程
    卸载1.在windows随便找一软件,右击,点击卸载。2.打开程序和功能窗口3.找到STM32CubeMX,双击运行卸载程序。4.勾选ForcethedeletionofD:ProgramFilesSTMicroelectronicsSTM32CubeSTM32cubeMx点击Uninstall5.点击是以确认删除STM32CubeMX6.等待......
  • stm32f407 cubemx lwip的简易服务器客户端收发项目
    元器件:野火stm32f407开发板技术:lwiptcp/ip内容:搭建的stm32服务器,可以接受和发送数据到客户端项目实现图片stm32通过串口发送数据客户端接受到数据客户端发送数据stm32收到数据**代码可以在我的主业进行下载**......
  • 六、STM32的外设OLED屏幕的使用
    介绍:OLED(有机发光二极管)是一种具有低功耗和快速响应特点的显示技术。本章所使用的0.96寸OLED模块,不仅界面占用少,且操作简便,非常适合嵌入式应用。其卓越的显示效果和高效的能源利用,使其成为理想的显示解决方案。阿里云盘分享提取码:c2s8目录一、OLED屏幕图示二、函数......
  • 金名的STM32F407标准库开发笔记(二)--点亮一个LED
    金名的STM32F407单片机标准库开发(二)–点亮LED灯​在金名的STM32F407单片机标准库开发(一)中我们编写了一段代码,相信即使是C语言大佬,初次看到这段代码也会很迷茫,无法理解这段代码的含义。确实,STM32单片机的代码,尤其是涉及到硬件操作的部分,对于初学者来说可能会显得有些复杂......
  • Linux驱动开发笔记(七):操作系统MMU介绍,操作系统操作寄存器的原理和Demo
    前言  做过单片机的都知道,写驱动是直接代码设置和读取寄存器来控制外设实现基本的驱动功能,而linux操作系统上是由MMU(内存管理单元)来控制,MMU实现了虚拟地址与芯片物理地址的对应,设置和获取MMU地址就是设置和获取映射的物理地址,从而跟单片机一样实现与物理硬件的驱动连接。 ......
  • STM32单片机芯片与内部39 DAC 数据手册 寄存器
    目录一、DAC寄存器1、DAC控制寄存器(DAC_CR)2、DAC软件触发寄存器(DAC_SWTRIGR)3、DAC通道1的12位右对齐数据保持寄存器(DAC_DHR12R1)4、DAC通道1的12位左对齐数据保持寄存器(DAC_DHR12L1)5、DAC通道1的8位右对齐数据保持寄存器(DAC_DHR8R1)6、DAC通道2的......
  • STM32单片机芯片与内部38 DAC 双DAC通道转换
    目录一、双DAC通道转换二、不使用波形发生器的独立触发三、使用相同LFSR的独立触发四、使用不同LFSR的独立触发五、产生相同三角波的独立触发六、产生不同三角波的独立触发七、同时软件启动八、不使用波形发生器的同时触发九、使用相同LFSR的同时触发十、使用不同LFS......
  • 汇编语言中的寄存器(8086)
    一:通用寄存器1.数据寄存器AX,BX,CX,DX,这四个寄存器在操作中可以存放各种类型的数据,但是他们存放的数据都有一定的规范,当我们使用loop指令时,他的循环次数受到CX里面数据的影响,CX里面存了什么数,就循环多少次,每循环一次又会将CX里面的值减一,当我们使用MUL和DIV指令时,他们都是单......
  • 毕业设计基于STM32F103C8T6智能小车设计PWM调速、红外循迹、障碍物跟随、超声波避障、
    (页数:61页、字数:14473字)1绪论1.1前言1.2设计任务与要求1.3智能小车硬件设计思路1.3.1智能小车控制板设计思路1.3.2智能小车底板设计思路2单片机的组成及特点2.1单片机的组成2.2单片机的特点2.3STM32F103C8T6单片机介绍2.4STM32F103C8T6单片......