开发环境:
IDE:MKD 5.30
开发板:野火启明6M5开发板
MCU:R7FA6M5BH3CFC
1 SCI 简介
SCI(Serial Communications Interface),串行通信接口,是串行通信技术的一种总称,包括了UART,SPI等串行通信技术。RA6M5的SCI模块是一个有6个通道的异步/同步串行接口。
SCI提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。SCI利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。使用多缓冲器配置的DMA方式,可以实现高速数据通信。
虽然SCI既可以同步又可以异步,但是常见的最常用的就是使用功能的异步功能,如果作为异步通信就是UART(Universal Asynchronous Receiver and Transmitter),可以说,UART是SCI的子集,但是同步通信相比异步通信多了一根时钟同步信号线。
下面简单介绍下同步和异步。
在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,见下图。通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。
在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,见下图,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。
在同步通讯中,数据信号所传输的内容绝大部分就是有效数据,而异步通讯中会包含有帧的各种标识符,所以同步通讯的效率更高,但是同步通讯双方的时钟允许误差较小,而异步通讯双方的时钟允许误差较大。
从上面的介绍可以看出,SCI以同步方式通信需要时钟同步信号,但不需要额外的起始、停止位,可以实现更快的传输速度。但SCI控制起来更复杂,因此本文主要讲解以异步通信,也就是UART。
2 串口硬件
串口的接口通过三个引脚与其他设备连接在一起。任何UART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。
- RX:接收数据串行输入。通过采样技术来区别数据和噪音,从而恢复数据。
- TX :发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O 口被同时用于数据的发送和接收。
由于PC和MCU的所用的电平不同,因此要想MCU和PC通信,需要以USB转串口的芯片,板载的芯片是CH340G。
3 串口发送实现
3.1 RA Smart Configurator配置UART
打开RA Smart Configurator,在配置界面里面依次打开“Pins->Peripherals->Connectivity:SCI->SCI9”配置SCI模块,配置为“Asynchronous UART”模式,并选择开发板所用的串口引脚,这里TX和RX分别接的是P109和P110引脚。
值得注意的是,板子的P109和P110与JTAG是复用的,因此这里需要事先关闭。
接下来就是添加串口stack。
接下来需要配置串口的参数。
这里可以设置串口的参数,我这里设置串口的变量名、通道以及相应的回调函数,SCI的编号和Channel编号是一一对应的,因此需要设置为9,回调函数依据C语言命名规范任意编译一个就行。
3.2 串口发送实现
实现是串口的初始化,代码很简单。
R_SCI_UART_Open (g_uart9.p_ctrl, g_uart9.p_cfg);
接下来就可以实现串口的发送了。
我们可以直接使用 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)
这里对发送函数进行封装。
void BSP_USART_Send_Byte(uint8_t ch)
{
R_SCI_UART_Write(g_uart4.p_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
}
void BSP_USART_Send_String_Length(uint8_t *str,uint32_t strlen)
{
unsigned int k=0;
do
{
BSP_USART_Send_Byte (*(str + k));
k++;
} while(k < strlen);
}
void BSP_USART_Send_String(uint8_t *str)
{
unsigned int k=0;
do
{
BSP_USART_Send_Byte (*(str + k));
k++;
} while(*(str + k)!='\0');
}
这样就方便多了,然后再主函数中调用发送函数。
char str[20];
sprintf(str,"20%02d-%02d-%02d",22,11,26);
BSP_USART_Send_String((uint8_t*)str);
最后编译下,查看效果。
下面笔者还要介绍一种常用的串口打印方式I/O重定向,也就是使用printf打印数据到终端,但是我们的裸机系统没有终端,因此如果想让printf 向UART发送数据,需要通过代码指定C标准库输出函数的控制终端设备,也就是使用功能I/O重定向。
下面我们以实现printf打印数据到UART(即重定义fputc函数)的实现过程。
volatile bool uart_send_complete_flag = false;
int fputc(int ch, FILE *f)
{
(void)f;
R_SCI_UART_Write(g_uart9.p_ctrl, (uint8_t *)&ch, 1);
while(uart_send_complete_flag == false);
uart_send_complete_flag = false;
return ch;
}
接下来就可使用printf函数了。
printf("Hello RA6M5\r\n");
将程序编译好下载到板子中,打开串口助手,按下图设置相应参数,按下板子的复位按键,在接收区可以看到如下信息。
另外,笔者在此给出printf输出格式的说明,请读者朋友参考。
格式 |
说明 |
%d |
按照十进制整型数打印 |
%6d |
按照十进制整型数打印,至少6个字符宽 |
%f |
按照浮点数打印 |
%6f |
按照浮点数打印,至少6个字符宽 |
%.2f |
按照浮点数打印,小数点后有2位小数 |
%6.2f |
按照浮点数打印,至少6个字符宽,小数点后有2位小数 |
%x |
按照十六进制打印 |
%c |
打印字符 |
%s |
打印字符串 |
3.3 串口中断接收实现
在RA Smart Configurator配置参数默认打开了中断,也配置了相应的回调函数,这里只需要实现即可。
void Uart4_Callback (uart_callback_args_t * p_args)
{
switch (p_args->event)
{
case UART_EVENT_RX_CHAR:
{
R_SCI_UART_Write(g_uart9.p_ctrl, (uint8_t *)&(p_args->data), 1);
break;
}
case UART_EVENT_TX_COMPLETE:
{
uart_send_complete_flag = true;
break;
}
default:
break;
}
}
在中断服务程序中,接收到数据后立即输出。
将程序编译好下载到板子中,打开串口助手,按下图设置相应参数,按下板子的复位按键,在接收区可以看到如下信息。