基于ST32F407ZGT6处理器在RT-Thread 5.0.2上挂载CAN BUS总线设备

1、开发环境

序号

资源名称

描述

备注

1

RT-Thread Studio 2.2.7

睿赛德官方推出的基于Eeclipse二次开发的集成软件开发环境。

2

开发板

自制的基于STM32F407ZGT6处理器的开发板,具备多种外设接口:例如CAN、CH340 USB串口、USB Device接口等。

2、CAN BUS设备挂载过程

        第2步,使用STM32CubeMX配置CAN总线。

        点击CubeMX Setting启动STM32CubeMX软件工程配置工具,稍等片刻,等待软件启动完成。

        STM32CubeMX启动完成。

        点击选中CAN1,点击Activated激活CAN1总线。

       打开CAN BUS的中断设置,因为有时候在设计原理图时不一定会使用软件配置时的缺省引脚。

        点击GERNERTE CODE生成代码,等待代码生成结果。

        出现提示窗口后,可以点击Cloes关闭,然后关闭STM32CubeMX配置工具。

        打开cubemx->stm32f4xx_hal_conf.h文件,确认CAN总线的宏定义已经被打开。

        第3步,使用RT-Thread Setting配置工具配置CAN总线。

        双击RT-Thread Setting打开配置界面。

        找到CAN 设备设置项,启用CAN总线,然后点击RT-Thread Studio左上角的保存配置。

        第4步,修改board.h文件,添加启用CAN总线的宏定义。

        在board.h文件的末尾添加宏:

                #define BSP_USING_CAN

                #define BSP_USING_CAN1

        第5步,编译代码。

        出现了很对编译错误。

        跟踪到代码错误位置,发现这二个宏没有定义,匪夷所思,都5.0.2版本了,居然使用CAN总线这二个宏还没有定义。

        仔细一看,好像单词LISTEN拼写有误,被写成了LISEN,修改一下,再次编译。

        

        报错信息少了二个,看起来还有处关于二处LISTEN的拼写错误,继续定位并修改。

        定位到错误位置,修改拼写错误后,再次编译。

        报错信息少了二个,但还有6个错误,继续定位错误信息位置。

        定位到报错的结构体定义位置。

        结构体定义中没有hdr成员,但有hdr_bank成员,应该是换名字了,修改报错定位点的成员名称。

        再次编译软件工程。

        报错信息又减少了2条,继续定位剩余的报错信息位置。

        定位报错位置的结构体定位信息。

        结构体定义中,没有hdr成员,但有hdr_index成员,看来又是因为改名所导致的问题,修改报错位置的结构体成员名称。

        将pmsg->hdr改为pmsg->hdr_index,修改名称后再次编译。

        软件工程顺利编译通过。

        第6步,固件下载运行。

        固件运行后,在PC端的MSH窗口中输入命令:

                list device

        已经可以见到一个can1设备。

        第7步,添加一个CAN总线的测试代码。

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

/*
 * 程序清单:这是一个 CAN 设备使用例程
 * 例程导出了 can_sample 命令到控制终端
 * 命令调用格式:can_sample 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);

    // 设置回环工作模式
    res = rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void *)RT_CAN_MODE_LOOPBACK);

#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_index = -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_sample(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);
    /* 创建数据接收线程 */
    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");
    }

    while(1)
    {
        msg.id = 0x100;             /* ID 为 0x100 */
        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 数据 */
        rt_thread_mdelay(1000);
        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_sample, can device sample);

        本段的CAN总线测试代码与官方的参考代码有二点不同:

         将CAN总线设置成了回环模式,这样用一个板卡本身就可以完成测试了。

       发送测试代码部分的消息id值改为了0x100,这样可以与CAN总线硬件滤波器相匹配,以确保自己发出去的信息可以被自己接收。 

        编译代码下载运行。

        在PC端的MSH窗口中,接收到CAN总线发送的信息了。

        至此,基于RT-Thread 5.0.2操作系统的一个CAN总线设备已经挂载成功。

  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要在代码中包含 "driver/spi_master.h" 头文件。 接下来,需要使用 spi_bus_config_t 结构体来配置 SPI 总线,如下所示: ```c spi_bus_config_t bus_cfg={ .mosi_io_num=..., // MOSI引脚编号 .miso_io_num=..., // MISO引脚编号 .sclk_io_num=..., // SCLK引脚编号 .quadwp_io_num=-1, // WP引脚编号(如果没有则设置为-1) .quadhd_io_num=-1, // HD引脚编号(如果没有则设置为-1) .max_transfer_sz=..., // 最大传输大小 .flags=..., // SPI总线的标志 .intr_flags=... // SPI总线中断标志 }; spi_bus_initialize(SPI1_HOST, &bus_cfg, DMA_CHAN); ``` 其中,SPI1_HOST 是 SPI 总线的主机编号,可以设置为 SPI1_HOST 或 SPI2_HOST。 DMA_CHAN 是 DMA 通道的编号,如果不需要 DMA,则设置为 -1。 接下来,需要使用 spi_device_interface_config_t 结构体来配置 SPI 设备,如下所示: ```c spi_device_interface_config_t dev_cfg={ .command_bits=..., // 命令位数 .address_bits=..., // 地址位数 .dummy_bits=..., // 哑位数 .mode=..., // SPI模式 .duty_cycle_pos=..., // 时钟极性 .cs_ena_pretrans=..., // 传输前片选延迟时间 .cs_ena_posttrans=..., // 传输后片选延迟时间 .clock_speed_hz=..., // 时钟频率 .input_delay_ns=..., // 输入延迟时间 .spics_io_num=..., // 片选引脚编号 .flags=..., // SPI设备的标志 .queue_size=..., // SPI设备队列的大小 .pre_cb=..., // 传输前回调函数 .post_cb=... // 传输后回调函数 }; spi_device_handle_t spi; spi_bus_add_device(SPI1_HOST, &dev_cfg, &spi); ``` 其中,spi_device_handle_t 是 SPI 设备的句柄,可以通过它来控制 SPI 设备。 spi_bus_add_device 函数会返回一个 SPI 设备的句柄。 最后,就可以使用 spi_device_transmit 函数来传输数据了,如下所示: ```c spi_transaction_t trans={ .flags=..., // 传输标志 .cmd=..., // 命令 .addr=..., // 地址 .length=..., // 数据长度 .rxlength=..., // 接收数据长度 .tx_buffer=..., // 发送缓冲区 .rx_buffer=..., // 接收缓冲区 .user=... // 用户数据 }; spi_device_transmit(spi, &trans); ``` 其中,spi_transaction_t 结构体用来描述一个 SPI 传输,可以设置传输标志、命令、地址、数据长度、接收数据长度、发送缓冲区、接收缓冲区和用户数据等。 以上就是控制外部 SPI 设备的基本步骤,具体实现可以根据实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值