终于到了通信协议部分了,这节课学习串口UART接发数据,下节课就学习IIC接发数据。

一、Serial设置
1. Channel
通过查看用户手册,64引脚产品一共有2个单元,6个通道,最多可实现3个串口收发。

这里选择通道0的UART0,后面选择发送/接收功能。

打开UART0的Receive页,从上到下一个一个来讲。
Data length setting 数据长度,这个一般选8bit(一个字节);
Transfer direction setting 传输设置,LSB是低位先行,MSB是高位先行,一般选默认的LSB。
Parity setting 校验位设置,看需求,这里不选校验位。
Stop bit length setting 停止位长度,这里默认1bit。
Receive data level setting 接收数据空闲设置,一般默认高电平(Normal)为空闲状态。
Transfer rate setting 传输速度设置,就是选比特率是多少,根据需求进行选择。
Interrupt setting 中断设置,设置接受完数据时触发,接收完成中断 RXI0 的优先级。
Callback function setting ,接收完成中断、返回错误中断是否开启,这里默认开启,方便我们接收完数据后进行处理。
| 分组 | 选项 | 含义 |
|---|---|---|
| Channel | UART0 | 当前正在配置 SAU0 通道 0 作为 UART0 接收器 |
| Data length | 7 / 8 / 9 bits | 一帧数据位长度;8 bits 最常用 |
| Transfer direction | LSB / MSB | 位序:先传最低位 LSB(默认),或最高位 MSB |
| Parity | None / Zero / Odd / Even | 校验方式;None=无校验,Even=偶校验 |
| Stop bit | 1 bit fixed | RL78 接收时 固定 1 位停止位,不可改 |
| Receive data level | Normal / Reverse | 空闲电平极性:Normal=高电平空闲;Reverse=低电平空闲(极少用) |
| Transfer rate | Baudrate 9600 … | 期望波特率;下方给出实际误差 |
| Reception end interrupt priority (INTSR0) | Level 0~3 | 接收完成中断 RXI0 的优先级;0=屏蔽 |
| Reception error interrupt priority (INTSRE0) | Level 0~3 | 帧错误/溢出/奇偶错 中断优先级 |
| Callback function | Reception end | 接收完成时 用户回调名(默认 r_uart0_callback_receiveend) |
| Reception error | 接收出错时 用户回调名 |

Transmit 选项卡也是类似,不多讲解。
2. Pin assignment

从这里可以看到,TxD0和RxD0对应P11和P12引脚。
二、编写代码
1、r_cg_serial.c

看到自动生成的函数,和之前一样,_Create 函数不用编写,_Start需要放到Main函数的初始化函数中。 用到的是
MD_STATUS R_UART0_Send(uint8_t * const tx_buf, uint16_t tx_num);
(里面的形参用来存储要发送的数据,调用后数据自动发送)
MD_STATUS R_UART0_Receive(uint8_t * const rx_buf, uint16_t rx_num);
(里面的形参用来存储要接收的数据,调用后数据自动接收)
static void r_uart0_callback_receiveend(void);
接收完毕后进入中断,需要编辑中断函数里面的内容
static void r_uart0_callback_sendend(void);
发送完毕后进入中断,需要编辑中断函数里面的内容
需要注意,后面两个函数在r_cg_serial_user.c里面。
2. r_cg_serial_user.c


在这两个中断函数中,写入标志位置零,标志接收/发送完毕。
3. r_main.c

定义接收和发送数组,就可以调试运行了。
三、遇到的坑
1. 接收数据后进中断,但是RX_Buffer里面全是0
这个经过排查,是因为uint8_t const RX_Buffer[5] = {0};将RX_Buffer[5]定义为了只读数组,所以中断可以进,但是RX_Buffer[5]里面的数值不可修改。

********AI 生成******
MD_STATUS R_UART0_Receive(uint8_t * const rx_buf, uint16_t rx_num),调用这个函数时,用RX_Buffer给第一个参数(uint8_t RX_Buffer[5] = {0};)和(uint8_t const RX_Buffer[5] = {0};)有什么区别?
区别只在于 指针本身的“常量性”,对接收功能毫无影响,但对代码可读性与后续维护有语义差异。
🔍 函数原型
c
复制
MD_STATUS R_UART0_Receive(uint8_t * const rx_buf, uint16_t rx_num);
-
第一个参数类型是
uint8_t * const
→ 指针本身(地址)在函数体内不可改,但指向的内存可写(接收数据)。
✅ 两种写法的对比
表格
复制
| 调用写法 | 实际传入类型 | 是否合法 | 语义说明 |
|---|---|---|---|
uint8_t RX_Buffer[5] = {0};R_UART0_Receive(RX_Buffer, 5); | uint8_t * → 自动转 uint8_t * const | ✅ | 普通可写缓冲区,最常用 |
uint8_t const RX_Buffer[5] = {0};R_UART0_Receive(RX_Buffer, 5); | const uint8_t * → 转 uint8_t * const | ❌ 编译错误 | 试图把“只读内存”地址传给要“写入数据”的函数 |
⚠️ 关键点
-
uint8_t const RX_Buffer[]表示 元素只读,编译器把数组放在 flash 或只读段;
而R_UART0_Receive()必定会往里面写数据,因此 类型不匹配,直接报错。 -
函数形参的
const是 修饰指针本身,不是修饰指向的数据;
它保证 函数体内不会把 rx_buf 重新指向别的地址,但 要求实参指向的内存必须可写。
✅ 正确做法
c
复制
uint8_t RX_Buffer[5] = {0}; // 可写缓冲区
R_UART0_Receive(RX_Buffer, 5); // ✅ 合法
❌ 错误做法
c
复制
uint8_t const RX_Buffer[5] = {0}; // 只读
R_UART0_Receive(RX_Buffer, 5); // ❌ 编译错误:丢弃 const 限定
✅ 一句话总结
给
R_UART0_Receive()传参时,必须用可写数组 (uint8_t []),
const uint8_t []会编译失败,因为驱动需要把接收到的数据 写入 这块内存。
********AI 生成******

被折叠的 条评论
为什么被折叠?



