文章目录

  • 一、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
}

【瑞萨RA_FSP】UART 编程实战相关推荐

  1. 【瑞萨RA_FSP】SCL UART 串口通信

    文章目录 一.串口通信协议简介 1. 物理层 2. 协议层 二.SCI 简介 三.SCI的结构框图 四.UART波特率计算 一.串口通信协议简介 串口通讯(Serial Communication)是 ...

  2. 【瑞萨RA_FSP】GPT—— PWM功能详解

    文章目录 一.GPT比较匹配功能详解 1. 锯齿波PWM模式(普通PWM模式) 2. 三角波PWM模式1(波谷32位传输) 3. 三角波PWM模式2(波峰和波谷32位传输) 4. 三角波PWM模式3( ...

  3. 【瑞萨RA_FSP】外部中断

    文章目录 一.外部引脚中断 二.中断过程 三.按键外部中断 一.外部引脚中断 1. ICU框图 根据ICU的功能框图可以知道,首先需要配置IRQCR寄存器(IRQ Control Register,I ...

  4. 【瑞萨RA_FSP】CTSU——电容按键检测

    文章目录 一.1. 电容按键介绍 二.电容按键原理 三.瑞萨QE在电容按键上面的运用 四.电容按键实验 1. 硬件设计 2. FSP配置 3.复制文件 4.主函数 一.1. 电容按键介绍 电容式感应触 ...

  5. 【瑞萨RA_FSP】DMAC/DTC编程实战

    文章目录 一.DMAC存储器到存储器传输 二.DTC外部中断触发传输 一.DMAC存储器到存储器传输 1. FSP配置 打开该工程的 FSP 配置界面.然后按如图步骤加入 DMAC. 加入 DMAC ...

  6. 【瑞萨RA_FSP】常用存储器介绍

    文章目录 一.存储器种类 二. RAM存储器 1. DRAM 1.1 SDRAM 1.2 DDR SDRAM 2. SRAM 3. DRAM与SRAM的应用场合 三.非易失性存储器 1. ROM存储器 ...

  7. 【瑞萨RA_FSP】WiFi——ESP8266模块通讯

    文章目录 一.Wifi模块简介 二.ESP8266功能介绍 1. 通用输入/输出接口(GPIO) 2. 使用UART与WIFI通讯 3. ESP8266工作模式介绍 三.AT指令 四.实验:STA模式 ...

  8. 【瑞萨RA_FSP】GPT—— 通用PWM定时器

    文章目录 一.PWM简介 二.GPT简介 三.GPT的框图分析 1. 计数器 2. 周期设置和周期设置缓冲寄存器 3. 时钟输入 4. 控制寄存器 5. 比较器和比较/输入捕获寄存器 6. 中断请求信 ...

  9. 【瑞萨RA_FSP】DMAC/DTC——直接存储器访问与数据传输

    文章目录 一.DMAC和DTC模块简介 1. DMAC 特性 2. DTC 特性 二.DMAC 模块框图分析 三.DMAC 传输模式 1. 正常传输模式 2. 重复传输模式 3. 块传输模式 4. 重 ...

最新文章

  1. 深入理解ES6 - var-let-const
  2. java 网线串口开发_C++标准语言不断被开发,C++却走向了下坡路!
  3. BeagleBone Black教程之BeagleBone Black设备的连接
  4. 小学生python入门-小学生都开始学的Python编程到底是什么?
  5. [置顶]IFTTT与Google+是什么?ifttt怎么玩?
  6. excel单元格斜线_Excel技巧 | 如何绘制斜线表头
  7. C++之类与对象(2)
  8. 扒一扒那些奇葩的甲方吧
  9. antd 使用upload 组件,使用自定义上传行为,覆盖默认action 访问请求
  10. Linux-nginx安装
  11. myeclipse 创建和访问 servlet 项目
  12. Abaqus之地应力平衡分析步 Geostatic step
  13. 计算机软件能删除吗,怎么彻底清除电脑软件鲁大师?卸载对系统有影响吗?
  14. hdu1052 Tian Ji -- The Horse Racing
  15. 手机怎样识别图片中的文字?
  16. excel 一列的数据除以另一列
  17. SEPIC电源基本电路分析
  18. 为什么这么多人怼我?或许是这个原因
  19. 热力学分布用matlab,matlab在热物理学中的应用.doc
  20. sipdroid软件直接使用andriod打网络电话

热门文章

  1. 计算机留学美国ps,美国留学ps
  2. 读《Optimally Tuned Iterative Reconstruction Algorithms for Compressive Sensing》有感……
  3. 我windows 上面的苹果切换桌面插件
  4. 亚马逊登山扣标准要求 ASTM1774
  5. 企业需要什么样的全面预算管理?
  6. cocoscreator,RenderTexture实现残影效果
  7. 我当初是这么勾搭上一个PPMM的 (转载)
  8. java查询学生信息_分别显示女生_女生学生基本信息的代码_java课程设计学生信息管理系统_毕业论文.doc...
  9. Axure的布尔运算/发布与设置
  10. 2022年CMS建站系统市场数据报告