【瑞萨RA_FSP】UART 编程实战

news2025/1/16 16:58:33

文章目录

  • 一、UART收发回显
  • 二、UART指令控制RGB灯
  • 三、基于环形队列的UART收发回显


一、UART收发回显

UART只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留UART接口来实现与其他模块或者控制器进行数据传输, 比如GSM模块,WIFI模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。

我们经常使用UART来实现控制器与电脑之间的数据传输。这使得我们调试程序非常方便,比如我们可以把一些变量的值、 函数的返回值、寄存器标志位等等通过UART发送到串口调试助手,这样我们可以非常清楚程序的运行状态。

不仅仅可以将数据发送到串口调试助手,还可以在串口调试助手发送数据给控制器,控制器程序根据接收到的数据进行下一步工作。

首先,编写一个程序实现开发板与电脑通信,在开发板上电时通过UART发送一串字符串给电脑,然后开发板进入中断接收等待状态, 如果电脑有发送数据过来,开发板就会产生中断,在中断服务函数接收数据,并马上把数据返回发送给电脑。

1. 硬件设计
为利用 UART 实现开发板与电脑通信,需要用到一个USB转串口(UART)的芯片:CH340G。 CH340G 是一个USB总线的转接芯片,实现USB转UART、USB转lrDA红外或者USB转打印机接口,我们使用其USB转UART功能。 具体电路设计见下图

在下面的三块开发板的电路图中,CH340G的TXD引脚与MCU芯片 UART 的RXD引脚连接, CH340G的RXD引脚与MCU芯片 UART 的TXD引脚连接。CH340G芯片集成在开发板上,其地线(GND)已与控制器的GND连通。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

P511:SCL4 RXD
P512:SCL4 TXD

2. 软件设计

① FSP配置

在 FSP 配置界面里面点开 “Pins”-> “Peripherals”-> “Connectivity:SCI”-> “SCI4” 来配置SCI模块, 配置为 “Asynchronous UART” 模式,并选择开发板所使用的串口引脚,如下图。

在这里插入图片描述
在配置界面底部点击 “Stack”,如下图步骤加入串口UART:
在这里插入图片描述
如下图点击刚刚加入的窗口,在左下角的“属性”窗口中配置 名字(name)、通道(Channel)、回调函数(Callback)名字即可, 引脚(Pins)、波特率(Baud Rate)等其他的属性按照默认的配置即可。
在这里插入图片描述
在这里插入图片描述

使用 printf 函数时,需要使用到堆,默认情况下堆的大小为0,因此我们需要修改堆的大小。 可以在 FSP 配置界面中的“BSP”属性栏的“RA Common”中通过修改“Heap size”来设置堆区大小。 这里需要设置为 8 的整数倍,对于RA6M5推荐至少为4K(0x1000),如下图。

在这里插入图片描述
最后点右上角的 “Generate Project Content” 按钮,让软件自动生成配置代码。

② 串口初始化函数
FSP 配置并生成代码之后,首先需要使用 R_SCI_UART_Open 函数打开 SCI4 UART 模块, 我们把这层调用封装为一个 Debug_UART4_Init 函数,如下所示。

/* 调试串口 UART4 初始化 */
void Debug_UART4_Init(void)
{
   fsp_err_t err = FSP_SUCCESS;

   err = R_SCI_UART_Open (&g_uart4_ctrl, &g_uart4_cfg);
   assert(FSP_SUCCESS == err);
}

③ R_SCI_UART_Write函数
串口初始化完成之后,可以直接使用 R_SCI_UART_Write 函数来将字符串写入到串口输出,该函数的原型如下。

fsp_err_t R_SCI_UART_Write (uart_ctrl_t * const p_api_ctrl, uint8_t const * const p_src, uint32_t const bytes)
  • 参数 p_src 指向要写入的字符串首地址

  • 参数 bytes 为传入的要写入的字符的数目

在使用 R_SCI_UART_Write 函数需要注意的一些事项:
若使用了 R_SCI_UART_Write() 来发送数据, 在数据发送完成之后会导致 uart_send_complete_flag 这个标志位被置位, 因此程序在调用 R_SCI_UART_Write 函数之后需要等待 uart_send_complete_flag 标志位被置位, 然后将该标志位清零。否则当连续调用 R_SCI_UART_Write 函数时可能导致发送数据丢失。 建议使用后文所述的 printf 函数将数据发送到串口。

④ 串口中断回调函数
在前面的 FSP 配置步骤的时候,设置了串口中断回调函数的名字为: debug_uart4_callback。 设置这么一个函数的原因是:每当串口发送或者接收完成一个字符时,都会默认触发串口的中断, 而在串口中断中会调用函数 debug_uart4_callback,在函数里我们需要根据不同的中断情况进行相应的处理。

因此,也需要同时在代码里面定义并实现这么函数 debug_uart4_callback。 把这个函数放到文件“bsp_debug_uart.c”中,该函数代码如下所示。

其中,需要定义一个额外的标志变量 uart_send_complete_flag 来表示串口发送数据已完成。 变量 uart_send_complete_flag 必须加上volatile,否则可能被编译器优化。

/* 发送完成标志 */
volatile bool uart_send_complete_flag = false;

/* 串口中断回调 */
void debug_uart4_callback (uart_callback_args_t * p_args)
{
   switch (p_args->event)
   {
      case UART_EVENT_RX_CHAR:
      {
            /* 把串口接收到的数据发送回去 */
            R_SCI_UART_Write(&g_uart4_ctrl, (uint8_t *)&(p_args->data), 1);
            break;
      }
      case UART_EVENT_TX_COMPLETE:
      {
            uart_send_complete_flag = true;
            break;
      }
      default:
            break;
   }
}

⑤ 重定向printf输出到串口
虽然可以直接使用 R_SCI_UART_Write 函数来将字符串输出到串口, 但是这个函数在很多情况下没有 printf 函数那样方便。所以需要添加一段代码来将 printf 输出重定向到串口(UART4)。

将以下的代码添加到源文件“bsp_debug_uart.c”里面。 由于不同C库的 printf 函数的底层实现不同,这里使用条件编译选择我们需要重写的函数。

/* 重定向 printf 输出 */
#if defined __GNUC__ && !defined __clang__
int _write(int fd, char *pBuffer, int size); //防止编译警告
int _write(int fd, char *pBuffer, int size)
{
   (void)fd;
   R_SCI_UART_Write(&g_uart4_ctrl, (uint8_t *)pBuffer, (uint32_t)size);
   while(uart_send_complete_flag == false);
   uart_send_complete_flag = false;

   return size;
}
#else
int fputc(int ch, FILE *f)
{
   (void)f;
   R_SCI_UART_Write(&g_uart4_ctrl, (uint8_t *)&ch, 1);
   while(uart_send_complete_flag == false);
   uart_send_complete_flag = false;

   return ch;
}
#endif

⑥ hal_entry入口函数
C语言程序的入口函数 main 函数调用了 hal_entry 函数。 在 hal_entry 函数里面编写应用代码。

void hal_entry(void)
{
   /* TODO: add your own code here */

   LED_Init();         // LED 初始化
   Debug_UART4_Init(); // SCI4 UART 调试串口初始化

   printf("这是一个串口收发回显例程\r\n");
   printf("打开串口助手发送数据,接收窗口会回显所发送的数据\r\n");

   while(1)
   {
      LED1_ON;
      R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
      LED1_OFF;
      R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
   }


#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

首先调用 LED_Init 函数初始化板子上的 LED 灯,然后调用 Debug_UART4_Init 函数初始化 SCI4 UART 作为调试串口来使用。 之后就可以使用 printf 函数了,调用 printf 输出提示信息到串口。 接着在 while 循环里是一段让 LED1 每隔一秒钟闪烁的程序。

二、UART指令控制RGB灯

1. 串口中断回调函数
需要在串口中断回调函数,也就是 debug_uart4_callback 函数里判断接收到的字符, 并根据所接收到的不同字符做出不同的操作。 修改 debug_uart4_callback 函数的代码,如下所示。

/* 串口中断回调 */
void debug_uart4_callback (uart_callback_args_t * p_args)
{
   switch (p_args->event)
   {
      case UART_EVENT_RX_CHAR:
      {
            /* 根据字符指令控制RGB彩灯颜色 */
            switch (p_args->data)
            {
               case '1':
                  LED1_ON;
                  break;
               case '2':
                  LED2_ON;
                  break;
               case '3':
                  LED3_ON;
                  break;
               case '4':
                  LED1_OFF;
                  break;
               case '5':
                  LED2_OFF;
                  break;
               case '6':
                  LED3_OFF;
                  break;
               case '7':
                  LED1_ON; LED2_ON; LED3_ON;
                  break;
               case '8':
                  LED1_OFF; LED2_OFF; LED3_OFF;
                  break;
               default:
                  break;
            }
            break;
      }
      case UART_EVENT_TX_COMPLETE:
      {
            uart_send_complete_flag = true;
            break;
      }
      default:
            break;
   }
}

2. hal_entry入口函数
在 hal_entry 函数里面进行硬件初始化之后,首先打印提示信息,提醒用户从串口输入数字字符。 然后默认关闭所有 LED 灯,在 while 循环里什么都不做,等待用户的输入。

void hal_entry(void)
{
   /* TODO: add your own code here */

   LED_Init();         // LED 初始化
   Debug_UART4_Init(); // SCI4 UART 调试串口初始化

   printf("这是一个串口控制 LED 例程\r\n");
   printf("打开串口助手发送以下指令,控制 LED 的状态\r\n");
   printf ("\t指令   ------  状态\r\n ");
   printf ("\t 1   ------  LED1_ON\r\n ");
   printf ("\t 2   ------  LED2_ON\r\n ");
   printf ("\t 3   ------  LED3_ON\r\n ");
   printf ("\t 4   ------  LED1_OFF\r\n ");
   printf ("\t 5   ------  LED2_OFF\r\n ");
   printf ("\t 6   ------  LED3_OFF\r\n ");
   printf ("\t 7   ------  LED 全亮\r\n ");
   printf ("\t 8   ------  LED 全灭\r\n ");

   LED1_OFF; LED2_OFF; LED3_OFF;   //默认关闭所有 LED 灯

   while(1)
   {
   }

#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

三、基于环形队列的UART收发回显

在实际项目开发中,由于有些串口不具备FIFO(如SCI1和SCI2)或FIFO的buffer比较小, 这可能会在数据处理速度小于数据接收速度的时候,导致数据的丢失。因此可以设计一个队列来避免这一问题。 在本实验中,使用环形队列来实现实验1的串口收发回显,将串口接收到的数据暂存在队列中, 待完成一次接收后再将队列中的数据全部发出去。

队列是一种特殊的线性表,只允许在队列头(head)删除元素,在队列尾(tail)添加元素。 当队列添加一个元素,队列尾向后移动,当队列删除一个元素,同样,删除一个元素,队列头向后移动,如下图。
在这里插入图片描述
由于存储空间是有限的,如果使用线性队列,删除元素后就会空出一段存储空间,这会造成很大的浪费。 因此实际上更多使用环形队列。并不是说这段存储空间是环形的,而是头指针和尾指针到达存储空间末尾后会回到存储空间起点。 因此在逻辑上这是循环的,如下图。
在这里插入图片描述
1. 环形队列的实现

#define DATA_LEN    300 //队列缓存大小

typedef struct
{
   uint16_t head;   //头指针
   uint16_t tail;   //尾指针
   uint8_t data[DATA_LEN];  //队列数据
} Circular_queue_t;

extern Circular_queue_t Circular_queue; //环形队列全局变量


bool Queue_Init(Circular_queue_t *circular_queue);    //初始化队列
bool Queue_isEmpty(Circular_queue_t *circular_queue); //判断队列是否为空
bool Queue_isFull(Circular_queue_t *circular_queue);  //判断队列是否已满
bool Queue_Wirte(Circular_queue_t *circular_queue, uint8_t *string, uint16_t len); //写数据
bool Queue_Read(Circular_queue_t *circular_queue, uint8_t *string, uint16_t len);  //读数据
uint16_t Queue_HadUse(Circular_queue_t *circular_queue); //返回队列中数据的长度
uint16_t Queue_NoUse(Circular_queue_t *circular_queue);  //返回未使用数据的长度

2. 串口中断回调函数

/* 串口中断回调 */
void debug_uart4_callback (uart_callback_args_t * p_args)
{
   switch (p_args->event)
   {
      case UART_EVENT_RX_CHAR:
      {
            /* 接收到数据后马上写入队列中 */
            Queue_Wirte(&Circular_queue, (uint8_t*) &p_args->data, 1);
            break;
      }
      case UART_EVENT_TX_COMPLETE:
      {
            uart_send_complete_flag = true;
            break;
      }
      default:
            break;
   }
}

3. hal_entry入口函数

void hal_entry(void)
{
   /* TODO: add your own code here */

   uint8_t Read_Buffer[DATA_LEN];
   uint16_t Read_Length;

   LED_Init();         // LED 初始化
   Debug_UART4_Init(); // SCI4 UART 调试串口初始化
   Queue_Init((Circular_queue_t*)&Circular_queue); //环形队列初始化

   printf("这是一个串口环形队列例程\r\n");
   printf("打开串口助手发送数据 5 个及以上的数据,接收窗口会打印所发送的数据\r\n");

   while(1)
   {
      if (Queue_isEmpty(&Circular_queue) == false)  //判断队列中的数据不为空
      {
            Read_Length = Queue_HadUse(&Circular_queue);
            if( Read_Length >= 5)       // 如果队列中的数据大于等于5个,开始打印队列中的所有数据
            {
               printf("Read_Length=%d: ", Read_Length);
               memset(Read_Buffer, 0, DATA_LEN);
               /* 读出 Read_Length 个数据 */
               Queue_Read(&Circular_queue, Read_Buffer, Read_Length);
               printf("%s\r\n", Read_Buffer);
            }
      }

      R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
   }


#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/557325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity3D】反射和折射

1 前言 立方体纹理(Cubemap)和天空盒子(Skybox)中介绍了生成立方体纹理和制作天空盒子的方法,本文将使用立方体纹理进行采样,实现反射和折射效果。 立方体纹理采样原理:从世界坐标系的坐标原点出…

深入printf

目录 printf的定义 printf的使用 函数说明 说明符(specifier) flags(标志) width(最小宽度) .precision(精度) length(类型长度) 转义序列 printf的…

linux(软硬链接)

目录: 1.软连接 2.硬链接 ----------------------------------------------------------------------------------------------------------------------------- 1.软连接 linux当中有两个概念,一个是软连接,一个是硬链接,在学习…

Golang每日一练(leetDay0074) 词典类设计、单词搜索II

目录 211. 添加与搜索单词 - 数据结构设计 Design-add-and-search-words-data-structure 🌟🌟 212. 单词搜索 II Word Search ii 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日一练 专栏 Golan…

基于C#和Blazor开发的前后端分离框架

Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行。 开源地址 https://gitee.com/known/Known 开发环境 .NET 7VS2022 概述 基于C#和Blazor实现的快速开发框架,前后端分离…

【深度学习】- 作业4: 脑部MRI(核磁共振)图像分别

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

【数据结构与算法】- 周测四

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

三、尚医通医院管理实现

文章目录 三、医院管理实现1、医院列表1.1 医院列表api接口1.1.1 添加service分页接口与实现1.1.2 添加controller方法 1.2 service-cmn模块提供接口1.2.1添加service接口与实现1.2.2添加controller方法 1.3封装Feign服务调用1.3.1搭建service-client父模块1.3.2 搭建service-c…

微生物实验之分菌(细菌)

文章目录 1. 采集实验样本2. 对实验样本进行处理1. 土壤样本的处理2. 植物内生菌样本的处理 3. 接种4. 分离纯化5. 测16s6. 测全基因组7. 保藏菌株 分离细菌菌株 (分菌) 是微生物学实验中,很重要的一环,对于微生物资源来说尤为重要。分菌主要包含以下几个…

人工智能CNN 卷积神经网络结构(tensorflow代码实现)

MNIST是一个简单的视觉计算数据集,它是像下面这样手写的数字图片: MNIST 通过上期的分享,我们了解了手写数字识别的基本原理以及CNN卷积神经网络的基本原理,本期我们结合MNIST数据集,来用代码来实现CNN。(手写数字识别是TensorFlow人工智能最基础的案例,这个跟学习编程…

删除表单(form)元素中的某一个数据项操作实现

问题 对于表单(form)元素,只支持POST请求。若是需要删除表单(form)元素中的某一个数据项,最为严谨的方式是采用POST请求, 对于表单元素,如何转换为DELETE请求 详细问题 对于表单元…

AC规则-2

基于RAM的远程接口 安全元件的访问规则可以通过远程应用程序管理 (RAM) 更新命令进行管理。 因此,ARA-M 和 ARA-C 各自提供一个远程接口,允许在 ARA 中存储或删除访问规则。 访问控制数据的任何远程管理都应仅通过 [GP 卡规范] 定义的安全通道协议来完成…

『树莓派云台机器人』01. 使用手机控制机器人

目录 1. 检查是否已经开机,连接机器人wifi2. 安装树莓派控制app应用,直连模式连接机器人3. 机器人功能实现总结 欢迎关注 『树莓派云台机器人』 博客,持续更新中 欢迎关注 『树莓派云台机器人』 博客,持续更新中 动手组装等步骤请…

chatgpt赋能Python-python_erode

Python Erode:用Python实现图像腐蚀 图像处理是人工智能领域的重要分支,Python是一种广泛应用于机器学习和深度学习的编程语言,也是图像处理领域的主要开发语言之一。在Python中,我们可以使用许多不同的库和工具来处理图像。其中…

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-9

深入理解Java虚拟机:JVM高级特性与最佳实践-总结-9 虚拟机类加载机制类加载的过程准备解析字段解析 方法解析接口方法解析 虚拟机类加载机制 类加载的过程 准备 准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量&#xff09…

检测字符串中所有的字母是否都为大写(字符中的数字、符号和空格不起作用)isupper()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 检测字符串中所有的字母是否都为大写 (字符中的数字、符号和空格不起作用) isupper() 选择题 以下程序的运行结果是? print("【执行】print(PYTHON.isupper())&qu…

chatgpt赋能Python-python_endif

Python Endif: 程序员必备的关键字 如果你是一位有经验的Python程序员,那么你一定非常熟悉Python中的一个关键字:Endif。本文将深入介绍Endif,帮助程序员更好地理解其作用和用法。 什么是Endif Endif是Python中的一个关键字,它…

【自然语言处理】- 作业5: 智能问答在法律智能领域的应用

课程链接: 清华大学驭风计划 代码仓库:Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的,其分为四门课,包括: 机器学习(张敏教授) , 深度学习(胡晓林教授), 计算…

Java如何连接数据库

Java连接MySQL数据库的方法:首先下载解压得到jar库文件,并在对应的项目中导入该库文件;然后添加JDBC;接着在Mysql数据库中进行建表,和添加数据的操作;最后连接数据库并读取数据即可。 Java 连接 MySQL数据库需要驱动包,解压后得到…

springboot国际化多语言配置

文章目录 概要springboot项目为例1 新建路径/文件2 新建两个配置类 搞一个控制器测试总结 概要 项目中有时候会用到多语言的业务场景; 一般来说都是通过后端实现的,将先有内容替换为适用的环境语言; springboot项目为例 1 新建路径/文件 新建路径static/i18n新建文件: mess…