开发板官网https://www.edevkit.com/本系列教程目录瑞萨单片机开发教程[一] e2-studio DAP LINK开发环境搭建瑞萨单片机开发教程[二] Keil/RASC开发环境搭建瑞萨单片机开发教程[三] 新建RASC工程瑞萨单片机开发教程[四] GPIO输出——led实验瑞萨单片机开发教程[五] GPIO输入——按键key实验瑞萨单片机开发教程[六] ICU外部中断实验SCI UART 串口通信实验本章目标熟悉 RASC 配置 SCI 串口的操作使用掌握串口通信 API 的使用掌握串口中断回调的使用硬件设计本实验使用开发板的 SCI UART 串口通过 USB 转串口连接电脑实现数据的收发通信。软件设计新建工程复制一份04 GPIO-led修改文件夹名07 SCI-UART_Recv_send双击打开EBF_RA6M5.uvprojx工程通过 Keil 的新建文件新建uart.c和uart.h保存到目录下的 \src 文件夹下。FSP 配置使用 RASC 配置 UART 时需要进行两方面的配置引脚配置、UART 本身的配置。首先进行引脚的配置先打开原理图确认使用哪一个 UART比如引脚使用 TXD0/RXD0、TXD1/RXD1 等。Keil 界面选择Tools→RA Smart Configurator打开 RASC 界面。在 FSP 配置界面里面点开Pins→Peripherals→SCI→SCI0在Operation Mode选择Asynchronous UARTTXD0、RXD0 选择电路原理图设计的引脚。①选择Pins包含所有 IO 和外围设备。②选择Peripherals外围设备。③选择 SCI 的 SCI0串口 0。④选择引脚组别刚好用的 P410、P411属于 B 组混搭就选择Mixed。⑤operation Mode选择Asynchronous UART使能SCI。⑥选择TXD0RXD0发送接收引脚根据电路图配置。引脚通道配置完还需要添加串口的Stack模块。①选择Stack。②选择New Stack添加外设模块。③选择Connectivity模块。④选择UART功能点击选择新建一个模块。①新建了一个g_uart0 UART模块。②点击这个模块显示Properties属性界面。配置名字name、通道Channel、回调函数Callback名字即可引脚Pins、波特率Baud Rate等其他的属性按照默认的配置即可。属性描述Name名字根据读者需求设置即可。Channel通道根据SCI号设置即可例如实验使用SCI0则这里配置为通道0。Data Bits每个字word的比特bit数默认为 8bitsParity校验模式可选择Odd奇校验Even偶校验或None无校验。Stop Bits停止位可选 1 或 2bit。Baud Rate波特率Baud Rate Modulation波特率调制通过调整时钟周期以减少波特率与实际波特率之间的误差。Max Error(%)计算波特率时允许的最大百分比误差。Callback回调函数的名字根据用户需求设置即可。Receive Interrupt Priority接收中断优先级Transmit Data Empty Interrupt Priority发送数据空中断优先级Transmit End Interrupt Priority发送完成中断优先级Error Interrupt Priority错误中断优先级使用 printf 函数时需要使用到堆默认情况下堆的大小为 0需要修改堆的大小。可以在 FSP 配置界面中的BSP属性栏的RA Common中通过修改Heap size来设置堆区大小。这里需要设置为 8 的整数倍对于 RA6M5 推荐至少为 4K0x1000。配置完成之后可以按下快捷键Ctrl S保存 最后点击 “Generate Project Content” 按钮让软件自动生成配置代码即可。引脚配置信息引脚配置还是在pin_data.c文件里生成。constioport_pin_cfg_tg_bsp_pin_cfg_data[]{...省略{.pinBSP_IO_PORT_04_PIN_10,.pin_cfg((uint32_t)IOPORT_CFG_PERIPHERAL_PIN|(uint32_t)IOPORT_PERIPHERAL_SCI0_2_4_6_8)},{.pinBSP_IO_PORT_04_PIN_11,.pin_cfg((uint32_t)IOPORT_CFG_PERIPHERAL_PIN|(uint32_t)IOPORT_PERIPHERAL_SCI0_2_4_6_8)},};pin_cfg参数配置为IOPORT_CFG_PERIPHERAL_PIN也就是复用连接到芯片的其它模块而非 GPIO参数IOPORT_PERIPHERAL_SCI0_2_4_6_8表示它们被连接到 SCI 模块。UART配置信息UART 模块信息会在hal_data.c文件里生成。在 RASC 里指定了 UART 使用哪个 SCI 通道、指定了它的数据格式数据位/校验位/停止位、指定了波特率等信息这些配置信息都被放入一个uart_cfg_t结构体里面的内容跟配置时选择的参数一致。constuart_cfg_tg_uart0_cfg{.channel0,.data_bitsUART_DATA_BITS_8,.parityUART_PARITY_OFF,.stop_bitsUART_STOP_BITS_1,.p_callbackuart0_callback,.p_contextNULL,.p_extendg_uart0_cfg_extend,...省略};结构体g_uart0_cfg里引用到了另一个结构体g_uart0_cfg_extend里面含有时钟、FIFO、流量控制等信息。constsci_uart_extended_cfg_tg_uart0_cfg_extend{.clockSCI_UART_CLOCK_INT,.rx_edge_startSCI_UART_START_BIT_FALLING_EDGE,.noise_cancelSCI_UART_NOISE_CANCELLATION_DISABLE,.rx_fifo_triggerSCI_UART_RX_FIFO_TRIGGER_MAX,.p_baud_settingg_uart0_baud_setting,.flow_controlSCI_UART_FLOW_CONTROL_RTS,...省略};引用了另一个结构体g_uart0_baud_setting。在 RASC 里配置了波特率为 115200RASC 会计算出各个寄存器的值存放在结构体g_uart0_baud_setting里。baud_setting_tg_uart0_baud_setting{/* Baud rate calculated with 0.469% error. */.semr_baudrate_bits_b.abcse0,.semr_baudrate_bits_b.abcs0,.semr_baudrate_bits_b.bgdm1,.cks0,.brr53,.mddr(uint8_t)256,.semr_baudrate_bits_b.brmefalse};中断回调函数操作一个 UART 引脚时要先打开它openopen 函数会配置 UART然后再调用 read、write 函数读、写串口。需要注意的是sci_uart模块里的读、写函数只是启动读、写功能这些函数返回时并不表示读、写已经完成。后续的读、写操作是由中断函数实现的。在传输过程中中断函数会调用回调函数来处理各种状态传输完成出错。回调函数原型在hal_data.h中定义。voiduart0_callback(uart_callback_args_t*p_args);p_args参数是uart_callback_args_t结构体类型。typedefstructst_uart_callback_arg{uint32_tchannel;/// Device channel numberuart_event_tevent;/// Event code/** Contains the next character received for the events UART_EVENT_RX_CHAR, UART_EVENT_ERR_PARITY, * UART_EVENT_ERR_FRAMING, or UART_EVENT_ERR_OVERFLOW. Otherwise unused. */uint32_tdata;voidconst*p_context;/// Context provided to user during callback}uart_callback_args_t;参数里的event表示原因它是一个uart_event_t枚举类型。/** UART Event codes */typedefenume_sf_event{UART_EVENT_RX_COMPLETE(1UL0),//read函数启动接收后接收完毕UART_EVENT_TX_COMPLETE(1UL1),//发送完毕UART_EVENT_RX_CHAR(1UL2),//未用read启动接收但接收到了数据UART_EVENT_ERR_PARITY(1UL3),//校验错误UART_EVENT_ERR_FRAMING(1UL4),// Mode fault error eventUART_EVENT_ERR_OVERFLOW(1UL5),// FIFO溢出UART_EVENT_BREAK_DETECT(1UL6),// Break detect error eventUART_EVENT_TX_DATA_EMPTY(1UL7),//最后一个字节已经发送出去了但是还没发送完毕}uart_event_t;对于接收有 2 个 eventUART_EVENT_RX_COMPLETE、UART_EVENT_RX_CHAR。前者被调用的时机是调用 read 后接收到所有数据时后者被调用的时机是未调用 read 函数但是接收到了数据。对于发送有 2 个 eventUART_EVENT_TX_DATA_EMPTY、UART_EVENT_TX_COMPLETE。以发送 2 个字节的数据为例前者被调用的时机是第 1 个字节已经成功发送完毕第 2 个字节已经被移入发送器但是还没发送完毕后者被调用的时机是这 2 个字节都发送完毕。前者先被调用在最后一个字节被发送但是未发送完毕时被调用后者在最后一个字节也完全发送完毕后被调用。程序设计实现串口的数据收发通过 USB 串口连接开发板和电脑电脑端使用串口调试助手给开发板发送数据开发板把接收的数据发送回电脑端。打开 UART 设备R_SCI_UART_Open(g_uart0_ctrl,g_uart0_cfg);发送数据fsp_err_tR_SCI_UART_Write(uart_ctrl_t*constp_api_ctrl,uint8_tconst*constp_src,uint32_tconstbytes);串口初始化完成之后我们可以直接使用R_SCI_UART_Write函数来将字符串写入到串口输出。参数p_src指向要写入的字符串首地址参数bytes为传入的要写入的字符的数目读取数据fsp_err_tR_SCI_UART_Read(uart_ctrl_t*constp_api_ctrl,uint8_t*constp_dest,uint32_tconstbytes);参数p_dest指向要保存的数组首地址参数bytes为传入的要读取的字符的数目在使用R_SCI_UART_Write函数需要注意的一些事项对于普通的读写操作可以在回调函数里设置状态标记用来表示读、写是否已经完成。这样使用 read、write 函数启动读、写操作后就可以轮询这些状态以等待操作完成。staticvolatileintg_uart0_tx_complete0;staticvolatileintg_uart0_rx_complete0;voiduart0_callback(uart_callback_args_t*p_args){switch(p_args-event){caseUART_EVENT_TX_COMPLETE:{g_uart0_tx_complete1;break;}caseUART_EVENT_RX_COMPLETE:{g_uart0_rx_complete1;break;}default:{break;}}}voiduart0_wait_for_tx(void){while(!g_uart0_tx_complete);g_uart0_tx_complete0;}voiduart0_wait_for_rx(void){while(!g_uart0_rx_complete);g_uart0_rx_complete0;}在回调函数里设置全局变量g_uart0_tx_complete表示发送完毕。用户程序在调用 write 启动 UART 发送后就可以调用第 25 行的uart0_wait_for_tx以等待发送完毕。在回调函数里设置全局变量g_uart0_rx_complete表示接收完毕。用户程序在调用 read 启动 UART 接收后就可以调用第 30 行的uart0_wait_for_rx以等待接收完毕。重定向 printf 输出到串口调试程序时希望从串口打印一些数据或格式化的字符串查看程序运行情况使用R_SCI_UART_Write()函数只能发送字节数据很不方便可以添加重定向 printf 代码重写fputc()函数通过串口发送格式化字符串。/* 重定向 printf 输出 */#ifdefined __GNUC__!defined __clang__int_write(intfd,char*pBuffer,intsize);//防止编译警告int_write(intfd,char*pBuffer,intsize){(void)fd;R_SCI_UART_Write(g_uart4_ctrl,(uint8_t*)pBuffer,(uint32_t)size);while(uart_send_complete_flagfalse);uart_send_complete_flagfalse;returnsize;}#elseintfputc(intch,FILE*f){(void)f;R_SCI_UART_Write(g_uart4_ctrl,(uint8_t*)ch,1);while(uart_send_complete_flagfalse);uart_send_complete_flagfalse;returnch;}#endif程序编写在uart.h头文件里定义中断初始化函数数据发送函数接收扫描函数。#ifndef__BSP_UART_H#define__BSP_UART_H#includehal_data.h#includestdio.h//初始化uartvoiduart0_Init(void);//封装发送指定长度数据voiduart_send_data(uint8_t*data,uint16_tlen);//扫描接收情况超过8字节就把接收的数据发送回去voiduart_scan_rx(void);#endif在uart.c里实现初始化函数、中断回调函数数据发送函数接收扫描函数。中断函数处理数据的接收一般需要接收一帧完整数据就需要定义一个数组接收到一个字节就保存到数组。数据发送函数封装一个发送自定长度数据函数发送数据后等待发送完成标识才表示发送完成。接收扫描函数放在循环中扫描接收的数据长度达到8字节就把接收的数据发送回去。#includeuart.h//初始化uartvoiduart0_Init(void){fsp_err_terrFSP_SUCCESS;errR_SCI_UART_Open(g_uart0_ctrl,g_uart0_cfg);assert(FSP_SUCCESSerr);}staticvolatileintg_uart0_tx_complete0;//发送完成标识staticvolatileintg_uart0_rx_complete0;//接收完成标识uint8_trxBuf[32]{0};//接收数据数组uint16_trxCnt0;//接收数据长度// 串口中断回调voiduart0_callback(uart_callback_args_t*p_args){switch(p_args-event){caseUART_EVENT_RX_CHAR://接收到一个字节{//没有接收满继续接受接收满了不在接收if(rxCntsizeof(rxBuf)){rxBuf[rxCnt](uint8_t)p_args-data;}break;}caseUART_EVENT_TX_COMPLETE://发送完成{g_uart0_tx_complete1;break;}caseUART_EVENT_RX_COMPLETE://接收完成{g_uart0_rx_complete1;break;}default:break;}}//等待接收完成voiduart0_wait_for_tx(void){while(!g_uart0_tx_complete);g_uart0_tx_complete0;}//等待发送完成voiduart0_wait_for_rx(void){while(!g_uart0_rx_complete);g_uart0_rx_complete0;}//封装发送指定长度数据voiduart_send_data(uint8_t*data,uint16_tlen){//把串口接收到的数据发送回去R_SCI_UART_Write(g_uart0_ctrl,data,len);//等待接收完成uart0_wait_for_tx();}//扫描接收情况超过8字节就把接收的数据发送回去voiduart_scan_rx(void){if(rxCnt8){//发送uart_send_data(rxBuf,rxCnt);//清除接收的数据rxCnt0;memset(rxBuf,0,sizeof(rxBuf));}}// 重定向 printf 输出#ifdefined __GNUC__!defined __clang__int_write(intfd,char*pBuffer,intsize);//防止编译警告int_write(intfd,char*pBuffer,intsize){(void)fd;R_SCI_UART_Write(g_uart4_ctrl,(uint8_t*)pBuffer,(uint32_t)size);while(uart_send_complete_flagfalse);uart_send_complete_flagfalse;returnsize;}#elseintfputc(intch,FILE*f){(void)f;R_SCI_UART_Write(g_uart0_ctrl,(uint8_t*)ch,1);uart0_wait_for_tx();returnch;}#endif在hal_entry.c文件中添加头文件uart.h在hal_entry入口函数初始化 uart0并添加printf(uart init ok\r\n)一条调试语句循环中执行uart_scan_rx()扫描函数。voidhal_entry(void){/* TODO: add your own code here */led_Init();// LED 初始化uart0_Init();printf(uart init ok\r\n);while(1){LED1_TOGGLE;R_BSP_SoftwareDelay(1,BSP_DELAY_UNITS_SECONDS);//延时1秒//扫描uart接收uart_scan_rx();}#ifBSP_TZ_SECURE_BUILD/* Enter non-secure code */R_BSP_NonSecureEnter();#endif}下载验证首先通过 USB 串口连接电脑和开发板串口的 RX/TX 分别连接开发板的 TX/RX交叉连接电脑端打开串口调试助手波特率设置 115200。连接下载器将程序编译并下载到开发板之后按下复位按键来复位开发板程序启动初始化完会打印一句uart init ok。LED 闪烁说明程序正常运行在串口调试助手发送 16 进制 8 字节数据在接收串口窗口收到相同数据。