RT-Thread studio上创建一个STM32F103的CAN通讯功能

前言

(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)学习本文之前,建议先在裸机上实现CAN通讯,这样测试一下通讯是否硬件是否正常。

前期准备

创建RT-Thread标准版工程

(1)打开项目资源管理器。

在这里插入图片描述

(2)在项目资源管理器中右键 —> 新建 —> 项目。

在这里插入图片描述

(3)选择RT-Thread项目 —> 下一步。

在这里插入图片描述

(4)选择所需要的芯片型号

在这里插入图片描述

(5)选择这个项目,进行编译。

在这里插入图片描述

进行STM32CubeMX适配

(1)打开STM32CubeMX

在这里插入图片描述

(2)因为我是STlink下载器,所以进行如下配置

在这里插入图片描述

(3)我开发板上是使用的外部无源晶振(陶瓷晶振),因此RCC的外部高速时钟配置为Crystal/Ceramic Resonator

在这里插入图片描述

(4)因为RT-ThreadFinSH是默认使用的串口1,因此,我们需要打开串口1。

在这里插入图片描述

(5)官方推荐STM32F103系统主频为72MHZ,因此这里设置为72MHZ

在这里插入图片描述

(6)对外设初始化的文件单独生成.c/.h文件,最终生成代码,并且关闭STM32CubeMX

在这里插入图片描述

(7)打开RT-Thread studio,出现如下弹框,点击确定。
注意:上面的STM32CubeMX必须关闭,否则这个弹窗将不会出现。

在这里插入图片描述

(8)此时编译烧录即可

在这里插入图片描述

(9)此时上机打开串口工具测试,波特率115200。即可看到如下打印信息。

在这里插入图片描述

(10)在某些教程中,可能会需要你找到cubemx/Src/main.c,将生成的SystemClock_Config()函数复制到drivers/drv_clk.c中。但是我当前版本的RT-Thread studio成功的避免了这个问题,工程中存在两个main.c也可以正常编译,cubemx/Src/main.c中的main()函数也被__WEAK因此不会产生冲突。

增加板级LED支持

(1)因为我开发板上有两个LED,一个是PB5引脚,一个是PE5引脚。为了知道程序在正常运行,同时打印信息太多影响FinSH使用,因此我加上LED闪烁程序,把Hello RT-Thread!打印信息删除。

/*
 * Copyright (c) 2006-2024, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-02-21     RT-Thread    first version
 */

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <rtdevice.h>
#include <drv_common.h>

#define LED1_PIN        GET_PIN(B, 5)
#define LED2_PIN        GET_PIN(E, 5)

int main(void)
{
    int count = 1;

    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
    while (count++)
    {
        rt_pin_write(LED1_PIN, PIN_LOW);
        rt_pin_write(LED2_PIN, PIN_HIGH);
        rt_thread_mdelay(1000);
        rt_pin_write(LED1_PIN, PIN_HIGH);
        rt_pin_write(LED2_PIN, PIN_LOW);
        rt_thread_mdelay(1000);
    }

    return RT_EOK;
}

适配CAN通讯功能

(1)通过上面的步骤,我们已经成功的创建了一个基础模板工程了。现在再增加CAN通讯的驱动部分。

增加CAN驱动

使能CAN驱动

(1)按照如下步骤使能RT-Thread中的CAN驱动。

在这里插入图片描述

(2)配置完成之后,按Ctrl+S保存,出现一下弹框,等待完成。

在这里插入图片描述

(3)进入board.h文件,添加如下代码。

/*-------------------------- CAN CONFIG BEGIN --------------------------*/

#define BSP_USING_CAN
#define BSP_USING_CAN1
/*#define BSP_USING_CAN2*/

/*-------------------------- CAN CONFIG END --------------------------*/

在这里插入图片描述

STM32CubeMX使能CAN设备

(1)双击打开STM32CubeMX,按照下图使能CAN设备。
切记,配置完STM32CubeMX之后,一定一定记得将STM32CubeMX关闭!

在这里插入图片描述

(2)找到cubemx/Src/can.c文件,按照如下步骤添加构建。
<1>

在这里插入图片描述

<2>

在这里插入图片描述

<3>

在这里插入图片描述

增加CAN驱动代码

(1)此时我们需要找到RT-Thread官方的驱动代码。首先按照如下方式找到RT-Thread Studio的安装路径。

在这里插入图片描述

(2)例如,现在我们RT-Thread Studio的安装路径为D:\RT-Thread_Studio\soft\RT-ThreadStudio,那么我们只需要再这个路径后面加上\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers。在这个路径中找到drv_can.c文件,然后复制到当前的工程drivers目录下。

在这里插入图片描述

(3)然后再在\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers路径中找到drv_can.h文件,然后复制到当前的工程drivers\include目录下。

在这里插入图片描述

(4)此时进入RT-Thread studio,点击当前工程,右键,选择刷新,即可出现刚刚复制过来的两个驱动文件。

在这里插入图片描述

上机实操

(1)编译烧录,上机测试。输入list_device命令,出现can1设备。表面CAN驱动已经成功编译进入。

在这里插入图片描述

增加CAN命令使用例程

(1)通过上面的步骤,我们成功将CAN驱动编译进入了RT-Thread中。但是还没有完成真正意义上的CAN设备使用。此时我们可以增加一个CAN设备使用命令,用于测试。

添加CAN命令测试代码

(1)我们先在applications目录下增加一个can_test.c的文件
<1>

在这里插入图片描述

<2>

在这里插入图片描述

(2)将如下代码编写进入can_test.c文件中。

在这里插入图片描述

/*
 * 程序清单:这是一个 CAN 设备使用例程
 * 例程导出了 can_test 命令到控制终端
 * 命令调用格式:can_test can1
 * 命令解释:命令第二个参数是要使用的 CAN 设备名称,为空则使用默认的 CAN 设备
 * 程序功能:通过 CAN 设备发送一帧,并创建一个线程接收数据然后打印输出。
*/

#include <rtthread.h>
#include "rtdevice.h"

#define CAN_DEV_NAME       "can1"      /* CAN 设备名称 */

static struct rt_semaphore rx_sem;     /* 用于接收消息的信号量 */
static rt_device_t can_dev;            /* CAN 设备句柄 */

/* 接收数据回调函数 */
static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size)
{
    /* CAN 接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void can_rx_thread(void *parameter)
{
    int i;
    rt_err_t res;
    struct rt_can_msg rxmsg = {0};

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(can_dev, can_rx_call);

#ifdef RT_CAN_USING_HDR
    struct rt_can_filter_item items[5] =
    {
        RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x100~0x1ff,hdr 为 - 1,设置默认过滤表 */
        RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), /* std,match ID:0x300~0x3ff,hdr 为 - 1 */
        RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), /* std,match ID:0x211,hdr 为 - 1 */
        RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL),                  /* std,match ID:0x486,hdr 为 - 1 */
        {0x555, 0, 0, 0, 0x7ff, 7,}                                       /* std,match ID:0x555,hdr 为 7,指定设置 7 号过滤表 */
    };
    struct rt_can_filter_config cfg = {5, 1, items}; /* 一共有 5 个过滤表 */
    /* 设置硬件过滤表 */
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);
    RT_ASSERT(res == RT_EOK);
#endif

    while (1)
    {
        /* hdr 值为 - 1,表示直接从 uselist 链表读取数据 */
        rxmsg.hdr = -1;
        /* 阻塞等待接收信号量 */
        rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        /* 从 CAN 读取一帧数据 */
        rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
        /* 打印数据 ID 及内容 */
        rt_kprintf("ID:%x", rxmsg.id);
        for (i = 0; i < 8; i++)
        {
            rt_kprintf("%2x", rxmsg.data[i]);
        }

        rt_kprintf("\n");
    }
}

int can_test(int argc, char *argv[])
{
    struct rt_can_msg msg = {0};
    rt_err_t res;
    rt_size_t  size;
    rt_thread_t thread;
    char can_name[RT_NAME_MAX];

    if (argc == 2)
    {
        rt_strncpy(can_name, argv[1], RT_NAME_MAX);
    }
    else
    {
        rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);
    }

    /* 查找 CAN 设备 */
    can_dev = rt_device_find(can_name);
    if (!can_dev)
    {
        rt_kprintf("find %s failed!\n", can_name);
        return RT_ERROR;
    }

    /* 初始化 CAN 接收信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及中断发送方式打开 CAN 设备 */
    res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
    RT_ASSERT(res == RT_EOK);

    /* 设置 CAN 通信的波特率为 500kbit/s*/
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);

    /* 创建数据接收线程 */
    thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        rt_kprintf("create can_rx thread failed!\n");
    }

    msg.id = 0x78;              /* ID 为 0x78 */
    msg.ide = RT_CAN_STDID;     /* 标准格式 */
    msg.rtr = RT_CAN_DTR;       /* 数据帧 */
    msg.len = 8;                /* 数据长度为 8 */

    /* 待发送的 8 字节数据 */
    msg.data[0] = 0x00;
    msg.data[1] = 0x11;
    msg.data[2] = 0x22;
    msg.data[3] = 0x33;
    msg.data[4] = 0x44;
    msg.data[5] = 0x55;
    msg.data[6] = 0x66;
    msg.data[7] = 0x77;
    /* 发送一帧 CAN 数据 */
    size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
    if (size == 0)
    {
        rt_kprintf("can dev write data failed!\n");
    }

    // 更改后再发送十次
    for(rt_uint8_t send_ind = 0; send_ind < 10; send_ind++)
    {
        rt_thread_mdelay(1000);

        msg.data[0] = msg.data[0] + 0x01;
        msg.data[1] = msg.data[1] + 0x01;
        msg.data[2] = msg.data[2] + 0x01;
        msg.data[3] = msg.data[3] + 0x01;
        msg.data[4] = msg.data[4] + 0x01;
        msg.data[5] = msg.data[5] + 0x01;
        msg.data[6] = msg.data[6] + 0x01;
        msg.data[7] = msg.data[7] + 0x01;
        /* 发送一帧 CAN 数据 */
        size = rt_device_write(can_dev, 0, &msg, sizeof(msg));
        if (size == 0)
        {
            rt_kprintf("can dev write data failed!\n");
        }
    }

    return res;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can_test, can device sample);

上机测试

(1)打开CAN分析仪,将波特率设置为500Kbps

在这里插入图片描述

(2)输入can_test命令,CAN分析仪上出现11次数据打印。

在这里插入图片描述

可能会遇到的问题

To initialize device:can1 failed

(1)在测试过程中,有可能会出现如下问题。

在这里插入图片描述

(2)处理办法很简单,将cubemx/Src/can.c加入构建。

在这里插入图片描述

can dev write data failed!

(1)如果测试出现这个错误,就说明你的CAN分析仪没有打开,或者是CAN分析仪波特率设置错误。

在这里插入图片描述

(2)处理办法很简单,打开CAN分析仪,设置正确的波特率,然后板子复位即可。

在这里插入图片描述

assertion failed at function:rt_object_init

(1)注意,每次can_test命令只能执行一次,因为第二次执行can_test命令就会出现这个报错,之后程序卡死。

在这里插入图片描述

(2)处理办法很简单,板子重新复位启动。

参考

(1)C站:RT-Thread系列08——CAN设备(CAN收发)
(2)C站:RT-Thread studio 添加CAN通信功能
(3)C站:STM32CubeMX创建CAN通讯工程+图莫斯UTA0403使用
(4)RT-Thread官方文档:CAN 设备
(5)C站:RT-Thread studio创建一个STM32F103ZE的RT-Thread标准版模板工程

  • 25
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32F103是意法半导体(STMicroelectronics)生产的一款32位嵌入式微控制器,具有高性能和低功耗的特点。RT-Thread Nano是RT-Thread实时操作系统的一个轻量级版本,适用于资源受限的嵌入式系统。 在将RT-Thread Nano移植到STM32F103上之前,需要先了解RT-Thread Nano的架构和STM32F103的硬件特性。 首先,需要确保RT-Thread Nano的源代码和STM32F103的开发环境已经准备好。接着,根据STM32F103的芯片手册和引脚映射表,需要对RT-Thread Nano的硬件抽象层进行适配,确保操作系统可以正确地访问外设和中断。 其次,需要根据STM32F103的内存和存储器大小来合理配置RT-Thread Nano的内存管理器和文件系统。可以根据实际需求对系统进行裁剪,移除不必要的模块和功能,以减小系统的内存占用和代码体积。 然后,需要配置STM32F103的系统时钟和中断向量表,并在启动代码中初始化硬件资源和系统任务。可以参考RT-Thread Nano的官方文档和示例代码,根据具体的硬件平台和应用需求进行相应的设置和调试。 最后,进行系统的编译、烧录和调试。可以使用STM32F103的开发工具链和调试器,对编译后的固件进行烧录和调试,确保系统能够正确地启动和运行。 总结来说,将RT-Thread Nano移植到STM32F103上需要进行硬件适配、内存管理和系统配置等工作,最终通过编译、烧录和调试来验证移植的正确性。这样可以使得STM32F103可以运行使用RT-Thread Nano操作系统的应用程序,实现更灵活和可靠的嵌入式系统设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风正豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值