瑞萨单片机论坛
直播中

华仔stm32

3年用户 2897经验值
擅长:嵌入式技术
私信 关注
[经验]

【RA4M2设计挑战赛】RTT CAN智慧屏温湿度计

【RA4M2设计挑战赛】RTT sensor模块驱动hs3003
在这篇的基础上,这两天更新了RT-Thread 驱动CAN,将迪文的智慧屏作为显示终端。
先来个的效果图:
image.png

原来用FSP做了CAN的驱动:
【瑞萨RA4系列开发板体验】CAN网络电压采集系统

这次偿试用RTT来写CAN总线的驱动。由于RTT版本的原因,前后用了两天才把发送做好。接收还有一点没有调通,但是不影响演示。先在这里记录一下,一来为自己后面的作备份,二来如果有想用CAN的作一个交流。

RASC配置CAN

如下图所示,添加CAN0,选择P102、P103为CAN0的RX、TX

image.png

保存后生成工程。

menuconfig配置

由于Board文件夹下的Kconfig.h没有配置CAN所以要手工添加can
用编辑工具打开Kconfig.h。
image.png
添加如下配置信息:
image.png

menuconfig BSP_USING_CAN
            bool "Enable CAN"
            default n
            select RT_USING_CAN
            if BSP_USING_CAN
                config BSP_USING_CAN0
                    bool "Enable CAN0"
                    default n

                config BSP_USING_CAN1
                    bool "Enable CAN1"
                    default n
            endif

退出保存。
在rt-thread-master\bsp\renesas\ra4m2-eco文件夹下打开menuconfig对CAN0进行配置

Hardware Drivers config-->
       On-chip Peripheral Drivers --->
             ENABLE CAN--->
                    Eable CAN0

image.png
退出保存。然后执行scons --target=mdk5更新工程。

打开后工程后添加can_dewen文件夹,建立can_dewen.c/h,并把他们加入工程,添加头文件的引用:
image.png
can_dewen.c主要根据RTT官方示例修改而来,大家想学习的移步CAN设备 (rt-thread.org)
内容如下:

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

#define CAN_DEV_NAME       "can0"      /* 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;
}

int can_send(uint32_t humi,uint32_t temp)
{
    struct rt_can_msg msg = {0};
    rt_err_t res;
    rt_size_t  size;
    msg.id = 0x78;              /* ID 为 0x78 */
       msg.ide = RT_CAN_STDID;     /* 标准格式 */
       msg.rtr = RT_CAN_DTR;       /* 数据帧 */
       msg.len = 8;                /* 数据长度为 8 */
       /* 待发送的 8 字节数据 */
       msg.data[0] = (uint8_t)(temp/100);
       msg.data[1] = (uint8_t)(temp%100);
       msg.data[2] = (uint8_t)(humi/100);
       msg.data[3] = (uint8_t)(humi%100);
       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");
       }

       return res;
}

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_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(void)
{
    struct rt_can_msg msg = {0};
    rt_err_t res;
    rt_size_t  size;
    rt_thread_t thread;
    char can_name[RT_NAME_MAX];


    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");
    }

    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");
    }

    return res;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(can_sample, can device sample);

can_dewen.h内容如下:

#ifndef __CAN_DEWEN_H__
#define __CAN_DEWEN_H__
#include <rtthread.h>

int can_send(uint32_t humi,uint32_t temp);
int can_sample(void);	
#endif

主要是添加了初始化、发送数据的函数。
在hal_entry.app下面添加获取hs3003与发送迪文屏的功能函数:

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-12-7      Vandoul      first version.
 */
#include "rtthread.h"
#include "drivers/pin.h"
#include "hal_data.h"
#include "board.h"


#ifdef __cplusplus
extern "C"{
#endif
#include "bsp_api.h"
#include "can_dewen.h"
#include "sensor_renesas_hs300x.h"
#ifdef __cplusplus
}
#endif

#include "sensor_renesas_hs300x.h"

/*******************************************************************************************************************//**
 * This function is called when an CAN event is occurred and SET the respective flags.
 **********************************************************************************************************************/

i2c_master_event_t i2c_event ;
void sci_i2c_master_callback(i2c_master_callback_args_t * p_args)
{
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    if (NULL != p_args)
    {
        /* capture callback event for validating the i2c transfer event*/
        i2c_event = p_args->event;
    }
}
void hal_entry(void)
{
	  float humi_f;
    float temp_f;
    rt_kprintf("hal_entry run.\r\n");
    rt_pin_mode(BSP_IO_PORT_04_PIN_04, PIN_MODE_OUTPUT);
		can_sample();
    while (1)
    {
				hs300x_read_data(&temp_humi_dev,&humi_f,&temp_f);
				can_send((uint32_t)(humi_f * 100),(uint32_t)(temp_f*100));
        rt_pin_write(BSP_IO_PORT_04_PIN_04, !rt_pin_read(BSP_IO_PORT_04_PIN_04));
        rt_thread_mdelay(1000);
    }
}

这样编译下载后,用CAN分析仪接上就可以收到数据了:
image.png

【重点】其实RTT调用设备驱动是非常简单的事,但是我调通用了整整两天,一来是自己的经验不足,二来因为rtt版本不匹配,需要很强的错误查找与解决能力。

排错点记录

1、编译时drv_can.c的第207行msg_ra = can->callback_args->p_frame; 这里一直报错,提示没有p_frame这个参数。
image.png

解决办法

反复查找,后面用全局查找,到了好久,终于在这里找到了结构体的定义
image.png
在r_can_api.h的第155行,查到了st_can_callback_args回调结构体的声明,他的下面没有p_frame参数,但是看到了frame结构体。
image.png
我偿试用frame来替代p_frame。但是报引用地址错误,我加上*号后,其他的地方又有报错。这个错误一直折腾了昨天晚上1点。累得实在没有办法,就休息了。
今天我用RA6M4用RT-Thread Studio建了CAN的工程,后面查到他的工程下面是用p_frame参数,但是可以通过,追踪到r-can_api.h下面看到结构体里多了一个can_frame_t * p_frame; // DEPRECATED Pointer to the received frame.。我后来试用在typedef struct st_can_callback_args
加上 can_frame_t * p_frame; // DEPRECATED Pointer to the received frame.
image.png

就可以编译通过了。

但是不是这个原因,接收现在还是没有通过的。

头文件引用错误

这里的工程是.app的工程,在添加头文件时,一定要记得externC里面,我加在上面时,编译一直提示没有找到头文件,所以一定要加对位置:
image.png

【小结】作为RTT用熟悉了一定很高效,就是跟RA的匹配还是不很好,scons --target=mdk5后,很多头文件的引用就被重置了,又得一个一个添加,很不方便。
总的来说,CAN总线应该配置相比UART复杂,但是使用起来要比串口好一些,特别适合小8个字节以内的通信。

回帖(1)

杨永胜

2023-3-5 07:49:38
楼主你好,请问你现在那边can能接收吗?我现在这边目前can也是只能发送,不能接收,没有进接收中断。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分