【瑞萨FPB-RA6E2试用】基础功能使用1
本文介绍了基于瑞萨FPB-RA6E2开发板和Zephyr RTOS的嵌入式开发实践。首先展示了开发板硬件特性,包括内置J-Link仿真器和丰富外设。接着配置Zephyr开发环境,通过VSCode工具实现项目迁移。通过LED闪烁例程验证环境配置正确性后,进一步实现GPIO综合应用:利用按键中断控制LED0状态翻转,同时主循环定时切换LED1状态,并通过串口输出调试信息。实验成功演示了Zephyr操作
【转载说明】
本文首发于电子技术论坛,原文链接。
瑞萨开发板介绍
本次试用的是瑞萨FPB-RA6E2开发板,配备R7FA6E2BB3CFM微控制器,是专门用于各种原型开发的评估板,内置J-Link仿真器电路,极大地方便了开发者编写和调试程序。收到实物如下图所示。

这款开发板拥有丰富的外设功能和配置,如下图所示。
zephyr介绍
zephyr是由linux基金会托管的开源实时操作系统,采用Apache 2.0协议。使用zephyr API开发项目,可以在板间非常轻松的实现项目迁移。其主流的开发方式以命令行工具为主,类似于linux开发。
本次开发使用vscode工具,具体环境配置教程参考链接如下:
跟着上述教程可以轻松完成开发环境配置。
配置环境过程中遇到的问题:下载过程不稳定,因为有一些依赖包需要在github下载,可以链接不稳定,下载失败,重新下载,多次尝试。
vscode环境配置问题:我是使用python的虚拟环境进行开发,但是配置好依赖包以后,使用zephyr IDE运行命令,使用的是默认的bash,报没有安装相关库的错误警告,找不到west,直接创建软链接,将其放在默认路径进行跳转解决。(很少使用python和vscode,能跑就不想继续折腾了)。下面将正式上手开发,首先从点灯开始。
zephyr的LED工程
在配置好环境以后,需要验证配置的环境是否正常,生成的代码是否能够在开发板上成功跑通。在零基础的情况下,参考瑞萨RA × Zephyr 系列教程4生成Blinky Sample代码,具体操作步骤详见教程4。
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 1000
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
/*
* A build error on this line means your board is unsupported.
* See the sample documentation for information on how to fix this.
*/
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void)
{
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) {
return 0;
}
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) {
return 0;
}
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
上面是生成的main.c代码的全部内容,可以看到代码的格式与常见的STM32配置程序有所不同,在while(1)中的主要代码逻辑为进行led的电平翻转,并通过串口输出当前led的状态,翻转间隔时间为#define SLEEP_TIME_MS 1000。
直接将代码下载到开发板,观察led状态,可以看到led状态按照代码的逻辑进行闪烁,并在串口输出了led的状态,如下图所示:

成功跑通Blinky Sample例程,说明环境配置成功,并且能够顺利下载到开发板,那么现在就可以愉快地玩耍了。
GPIO功能测评
GPIO做为一个开发板的常用对外接口,通常连接外部LED或按键,瑞萨FPB-RA6E2这款开发板拥有两个用户LED和一个用户按键。具体分布如下图所示:

本次实验:
功能:LED、按键
效果:按键中断控制LED1的翻转,主循环延时控制LED2的翻转(时间为1s)
实现代码如下所示:
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* 1. 获取设备树节点:LED0、LED1 和 SW0 (按键) */
#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
#define SW0_NODE DT_ALIAS(sw0)
/* 2. 定义 GPIO 结构体 */
static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
/* 获取按键的 GPIO 信息,注意这里增加了 GPIO_IN 标志的获取 */
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
/* 3. 定义中断回调结构体变量 */
static struct gpio_callback button_cb_data;
/* 4. 中断服务函数 (按键按下后会自动执行这里) */
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
/* 翻转 LED 状态 */
gpio_pin_toggle_dt(&led0);
printf("按键已按下!LED 状态翻转。\n");
}
int main(void)
{
int ret;
/* A. 初始化 LED */
if ((!gpio_is_ready_dt(&led0))||(!gpio_is_ready_dt(&led1))) {
printf("Error: LED device not ready\n");
return 0;
}
/* 配置 LED 为输出,初始状态为灭 (INACTIVE) */
ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE);
if (ret < 0) return 0;
ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_INACTIVE);
if (ret < 0) return 0;
/* B. 初始化 按键 */
if (!gpio_is_ready_dt(&button)) {
printf("Error: Button device not ready\n");
return 0;
}
/* B1. 配置按键为输入 (Input) */
/* * GPIO_INPUT: 输入模式
* GPIO_PULL_UP: 启用上拉电阻 (防止按键悬空乱跳)
*/
ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
if (ret < 0) {
printf("Error: Failed to configure button gpio\n");
return 0;
}
/* B2. 配置中断触发方式 */
/* GPIO_INT_EDGE_TO_ACTIVE: 当按键被按下那一瞬间(边沿触发)产生中断 */
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
if (ret != 0) {
printf("Error: Failed to configure interrupt\n");
return 0;
}
/* B3. 注册中断回调函数 */
gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
gpio_add_callback(button.port, &button_cb_data);
printf("按键控制 LED 程序已启动。请按板子上的 S1 键。\n");
/* 主循环 */
while (1) {
k_msleep(1000);
gpio_pin_toggle_dt(&led1);
}
return 0;
}
在代码中注册自己编写的中断处理函数,进入一次中断就翻转一次LED0的状态,同时使用串口输出信息。在主循环里面定时翻转LED1的状态,表示程序运行正常。

上图为串口输出内容,随着按键的按下,正确显示了中断函数里面的输出内容。
UART功能测试
前面的试验中已经展示了输出功能,串口不仅可以输出日志信息,还可以接收外部传递的信息。这个试验将展示使用串口的接收功能来控制LED灯的亮灭。
| 串口接收信息 | LED灯状态 |
|---|---|
| ON | 亮 |
| OFF | 灭 |
| TOGGLE | 翻转 |
同时要在prj.conf文件中启用相关配置
启用控制台子系统
CONFIG_CONSOLE_SUBSYS=y
启用行读取功能
CONFIG_CONSOLE_GETLINE=y
主程序如下所示:
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/console/console.h>
#include <string.h>
/* --- 1. 定义 LED 硬件资源 --- */
/* 获取设备树中定义的 led0 (对应板子上的 LED1) */
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void)
{
int ret;
/* --- 2. 初始化 LED --- */
if (!gpio_is_ready_dt(&led)) {
printk("Error: LED device not ready\n");
return 0;
}
/* 配置为输出,默认关闭 */
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
if (ret < 0) return 0;
/* --- 3. 初始化控制台 (行读取模式) --- */
/* 注意:使用的是 console_getline_init() 而不是 console_init() */
console_getline_init();
printk("\n=== Zephyr LED Controller ===\n");
printk("Type 'ON', 'OFF' or 'TOGGLE' and press Enter.\n");
while (1) {
/* 打印提示符 */
printk("uart:~$ ");
/* --- 4. 获取一整行输入 (阻塞等待) --- */
char *s = console_getline();
/* --- 5. 解析命令 --- */
/* 这里的 s 就是完整的一行字符串,直接比较即可 */
if (strcmp(s, "ON") == 0) {
gpio_pin_set_dt(&led, 1);
printk("Result: LED Turned ON\n");
}
else if (strcmp(s, "OFF") == 0) {
gpio_pin_set_dt(&led, 0);
printk("Result: LED Turned OFF\n");
}
else if (strcmp(s, "TOGGLE") == 0) {
gpio_pin_toggle_dt(&led);
printk("Result: LED Toggled\n");
}
else {
/* 只是打印出来,不做操作 */
printk("Unknown command: '%s'\n", s);
}
}
return 0;
}
通过串口调试助手发送信息,查看开发板上LED灯的状态。

通过这个串口调试助手,可以看出,开发板能够正确接收到发送的数据并正确解析,执行LED的状态控制程序。
zephyr是一个实时操作系统,意味不像裸机一样只能单线程工作,可以执行多线程任务,在前面的UART程序中,使用的 char *s = console_getline();会使的程序停留在这里等待内容输入,并不执行后面的程序。因此可以单独创建一个线程运行LED1的闪烁任务,标志系统运行正常,然后在主线程里面还是使用UART控制LED0的状态。
具体程序如下所示:
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/console/console.h>
#include <string.h>
/* --- 1. 定义 LED 硬件资源 --- */
#define LED0_NODE DT_ALIAS(led0)
#define LED1_NODE DT_ALIAS(led1)
static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
/* --- 2. 定义新线程的参数 --- */
/* 栈大小:1024字节对于简单的点灯足够了 */
#define BLINK_STACK_SIZE 1024
/* 优先级:7 (Zephyr中数字越小优先级越高,7是常用应用优先级) */
#define BLINK_PRIORITY 7
/* --- 3. 编写 LED1 闪烁线程的入口函数 --- */
/* 这是一个独立的死循环,专门负责 LED1 */
void blink_led1_entry(void *p1, void *p2, void *p3)
{
int ret;
/* 在线程内部初始化 LED1,确保资源独立 */
if (!gpio_is_ready_dt(&led1)) {
return;
}
ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);
if (ret < 0) return;
while (1) {
gpio_pin_toggle_dt(&led1);
/* 这个线程每 1000ms 醒来工作一次 */
printk("[Thread 1] LED Toggled\n");
k_msleep(1000);
}
}
/* --- 4. 使用宏静态创建并启动线程 --- */
/* * K_THREAD_DEFINE(
* 线程名, 栈大小, 入口函数,
* 参数1, 参数2, 参数3,
* 优先级, 选项, 延迟启动时间
* )
*/
K_THREAD_DEFINE(blink_tid, BLINK_STACK_SIZE, blink_led1_entry, NULL, NULL, NULL,
BLINK_PRIORITY, 0, 0);
/* --- 5. 主线程 (main) --- */
/* main 函数本身就是一个线程,我们用它来处理串口输入 */
int main(void)
{
int ret;
/* 初始化 LED0 */
if ((!gpio_is_ready_dt(&led0))) {
printk("Error: LED device not ready\n");
return 0;
}
ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_INACTIVE);
if (ret < 0) return 0;
/* 初始化控制台 (行读取模式) */
console_getline_init();
printk("\n=== Zephyr Multi-thread Demo ===\n");
printk("Thread 1: Blinking LED1 automatically.\n");
printk("Thread 2: Waiting for UART commands for LED0.\n");
while (1) {
printk("uart:~$ ");
/* 这里依然会阻塞,但只会阻塞 main 线程 */
/* blink_led1_entry 线程依然会在后台欢快地运行 */
char *s = console_getline();
if (strcmp(s, "ON") == 0) {
gpio_pin_set_dt(&led0, 1);
printk("[Thread 2] Result: LED0 Turned ON\n");
}
else if (strcmp(s, "OFF") == 0) {
gpio_pin_set_dt(&led0, 0);
printk("[Thread 2] Result: LED0 Turned OFF\n");
}
else if (strcmp(s, "TOGGLE") == 0) {
gpio_pin_toggle_dt(&led0);
printk("[Thread 2] Result: LED0 Toggled\n");
}
else {
printk("[Thread 2] Unknown command: '%s'\n", s);
}
/* 不需要 k_msleep,console_getline 的阻塞就是最好的让渡 CPU 的方式 */
}
return 0;
}
在这个里面发现线程的创建并不是在main函数里面,而是在外面,点进去一看,使用的是#define,

在编译的时候就创建了这个子线程。

可以看出两个线程独立运行, char *s = console_getline();不能影响到LED1的闪烁。
更多推荐



所有评论(0)