[RTT & Renesas high performance CPK-RA6M4 development board review] - Serial and USB communication
[Copy link]
This article continues to talk about serial port and USB communication, mainly talking about the configuration of serial port and USB-CDC class, and briefly talking about the use of serial port and USB-CDC.
First, the configuration of the serial port. Open the graphical configuration tool, find Peripherals->SCI in the Pins setting, and then select one of SCI0-SCI9. You can configure the Operation Mode to Asynchronous UART mode in Pin Configuration, which is the full-duplex serial port we often use. Then you can configure the pin assignment of TXD0 and RXD0.
I chose the two pins P101 and P410. These two pins are next to each other on the evaluation board, as shown below
After selecting the pins to be used, we need to assign stacks to the serial port. Open the Stacks page
Click 1. New Stack to open a drop-down list. Find Connectivity->UART(r_sci_uart) in it and click it to generate the stack in 2.
Then click the 1 in the upper right corner to switch the perspective to the graphical configuration tool interface, and then click the stack module we just added, and you can see the property configuration interface in the lower left corner. I have to complain that the perspective switching of this editor is really not smart, and I can't switch the configuration interface by myself. I don't know if it's just me.
For the configuration of this property, temporarily use the contents in the red box in the figure below. The contents in 1 do not need to be changed. The first item in 2 is the baud rate, which can be determined according to the specific situation. The name of the serial port callback function after Callback in 3 is a name that is easy to recognize. For example, I wrote user_uart_callback. The configuration of the serial port is now completed.
Then let's talk about the configuration of USB-CDC.
Still the same, the first step is to configure the USB clock, open the Clock page, the arrow in the figure below indicates the source of the USB clock, we change PLL2 Src to: HOCO, change the values of PLL2 Div and PLL2 Mul, select UCLK Src as PLL2, change the value of UCLK Div, and finally make the USB clock UCLK 48MHz.
Then comes the pin configuration. Find the USB in the figure below and select Device as the Operation Mode, the drive mode.
Then switch to the Stack interface and add USB_PCDC (r_usb_pcdc)
Click on the newly created stack module, and you can see the relevant configuration about USB in the properties. No modification is made here.
Then click the run icon in the upper right corner to get the updated code.
The serial port and USB are configured, so let's talk about the code. What I need to achieve this time is to send commands to the microcontroller through the serial port, the microcontroller turns on or off the LED, and sends the LED status to the virtual serial port through USB.
The first is the serial port. For the serial port, we need to know the following three functions, namely, opening the serial port, reading data from the serial port, and sending data from the serial port.
fsp_err_t R_SCI_UART_Open(uart_ctrl_t *const p_api_ctrl, uart_cfg_t const *const p_cfg)
fsp_err_t R_SCI_UART_Read(uart_ctrl_t *const p_api_ctrl, uint8_t *const p_dest, uint32_t const bytes)
fsp_err_t R_SCI_UART_Write(uart_ctrl_t *const p_api_ctrl, uint8_t const *const p_src, uint32_t const bytes)
R_SCI_UART_Open() This function needs to be used when the program is running to enable the serial port. I put it at the beginning of the hal_entry() function.
I also found the receiving and sending parts of the serial port on the Internet. I suggest you take a look at this blog. This blog talks about how to redirect the printf function and how the serial port sends and receives data.
For me, I only receive data of a certain length for the time being. I will post my serial port processing part first.
In the serial port file usart_printf.c, the serial port processing part:
#include <stdio.h>
#include "hal_data.h"
#include "usart_printf.h"
uint8_t RxBuff[1]; //进入中断接收数据的数组
uint8_t DataBuff[500]; //保存接收到的数据的数组
int RxLine=0; //接收到的数据长度
int Rx_flag=0; //接受到数据标志
int Rx_flag_finish=0; //接受完成
fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
void user_uart_callback (uart_callback_args_t * p_args)
{
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
if(p_args->event == UART_EVENT_RX_CHAR)
{
RxBuff[0] = (uint8_t)p_args->data;
RxLine++; //每接收到一个数据,进入回调数据长度加1
DataBuff[RxLine-1]=RxBuff[0]; //把每次接收到的数据保存到缓存数组
Rx_flag=1;
if(RxBuff[0]=='\n') //接收结束标志位,这个数据可以自定义,根据实际需求,这里只做示例使用,不一定是0xff
{
Rx_flag_finish=1;
}
RxBuff[0]=0;
}
}
void printf_usart(void)
{
printf("length=%d\r\n",RxLine);
for(int i=0;i<RxLine;i++)
printf("data:[%d] = 0x%x\r\n",i,DataBuff[i]);
memset(DataBuff,0,sizeof(DataBuff)); //清空缓存数组
//memset()作用:可以方便的清空一个结构类型的变量或数组。
//例句:memset(aTxbuffer,0,sizeof(aTxbuffer)) 用memset清空aTxbuffer。
RxLine=0; //清空接收长度
Rx_flag_finish=0;
Rx_flag = 0;
}
Serial port redirection part,
#ifdef __GNUC__ //串口重定向
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
err = R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t *)&ch, 1);
if(FSP_SUCCESS != err) __BKPT();
while(uart_send_complete_flag == false){}
uart_send_complete_flag = false;
return ch;
}
int _write(int fd,char *pBuffer,int size)
{
for(int i=0;i<size;i++)
{
__io_putchar(*pBuffer++);
}
return size;
}
The code of the serial port part is basically the same as that in the reference blog. In user_uart_callback (uart_callback_args_t * p_args), in this function, I changed the serial port receiving end mark to '\n', so that the string can be accepted.
For the USB-CDC code, refer to the Renesas manual.
Four variables need to be created:
/* Global variables for the USB */
usb_status_t usb_event; //USB状态
usb_setup_t usb_setup; //USB事件
uint8_t g_usb_module_number = 0x00; //USB模块编号
usb_class_t g_usb_class_type = 0x00; //USB类
Then comes the USB initialization:
//CDC串口参数
static usb_pcdc_linecoding_t g_line_coding;
//开启USB
R_USB_Open (&g_basic0_ctrl, &g_basic0_cfg);
R_USB_ClassTypeGet (&g_basic0_ctrl, &g_usb_class_type);
R_USB_ModuleNumberGet (&g_basic0_ctrl, &g_usb_module_number);
Since no callback function is used, the USB status is read in a loop in the main function:
/* Obtain USB related events */
R_USB_EventGet (&g_basic0_ctrl, &usb_event);
Then parse the status:
/* USB event received by R_USB_EventGet */
if (usb_event == USB_STATUS_REQUEST)
{
R_USB_SetupGet (&g_basic0_ctrl, &usb_setup);
if (USB_PCDC_SET_LINE_CODING == (usb_setup.request_type & USB_BREQUEST))
{
/* Configure virtual UART settings */
R_USB_PeriControlDataGet (&g_basic0_ctrl, (uint8_t*) &g_line_coding, LINE_CODING_LENGTH);
}
else if (USB_PCDC_GET_LINE_CODING == (usb_setup.request_type & USB_BREQUEST))
{
/* Send virtual UART settings back to host */
R_USB_PeriControlDataSet (&g_basic0_ctrl, (uint8_t*) &g_line_coding, LINE_CODING_LENGTH);
}
else if (USB_PCDC_SET_CONTROL_LINE_STATE == (usb_setup.request_type & USB_BREQUEST))
{
/* Acknowledge all other status requests */
R_USB_PeriControlStatusSet (&g_basic0_ctrl,
USB_SETUP_STATUS_ACK);
}
else
{
}
}
In the parsing state, you can get the state of USB reading data, which can be used when USB receives data. I don't use USB to receive data for the time being, but only use USB to send data, which is relatively simple.
fsp_err_t R_USB_Write(usb_ctrl_t *const p_api_ctrl, uint8_t const *const p_buf, uint32_t size, uint8_t destination)
The above function is used when USB sends data. USB sends data as shown below:
if(Rx_flag_finish == 1){
Rx_flag_finish = 0;
printf("%s", DataBuff);
if (memcmp(DataBuff, "LED on\r\n", 8) == 0){
strcpy (send_str, "LED is on\r\n");
led_level = BSP_IO_LEVEL_HIGH;
g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, led_level);
R_USB_Write(&g_basic0_ctrl, (uint8_t*) send_str, ((uint32_t) strlen(send_str)), (uint8_t) g_usb_class_type);
}
else if(memcmp(DataBuff, "LED off\r\n", 8) == 0){
strcpy (send_str, "LED is off\r\n");
led_level = BSP_IO_LEVEL_LOW;
g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, led_level);
R_USB_Write(&g_basic0_ctrl, (uint8_t*) send_str, ((uint32_t) strlen(send_str)), (uint8_t) g_usb_class_type);
}
printf_usart();
}
In the above code, I compared the serial port data, recognized the control commands "LED on\r\n" and "LED off\r\n", changed the LED state, and sent the LED state to the virtual serial port through USB.
The complete code contains three documents:
hal_entry.c
#include "hal_data.h"
#include "usart_printf.h"
#define LINE_CODING_LENGTH (0x07U)
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER
/* Global variables for the USB */
usb_status_t usb_event; //USB状态
usb_setup_t usb_setup; //USB事件
uint8_t g_usb_module_number = 0x00; //USB模块编号
usb_class_t g_usb_class_type = 0x00; //USB类
/* Global variables for the program */
static char send_str[20] = { "LED on\r\n" }; //发送字符串
static volatile uint8_t sw1_pressed = false; //触发转换标志
static uint8_t led_level = BSP_IO_LEVEL_HIGH; //LED状态
/*******************************************************************************************************************//**
* main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
* is called by main() when no RTOS is used.
**********************************************************************************************************************/
void hal_entry(void)
{
/* TODO: add your own code here */
//开启串口
R_SCI_UART_Open(&g_uart0_ctrl, &g_uart0_cfg);
//CDC串口参数
static usb_pcdc_linecoding_t g_line_coding;
//开启USB
R_USB_Open (&g_basic0_ctrl, &g_basic0_cfg);
R_USB_ClassTypeGet (&g_basic0_ctrl, &g_usb_class_type);
R_USB_ModuleNumberGet (&g_basic0_ctrl, &g_usb_module_number);
while(1){
/* Obtain USB related events */
R_USB_EventGet (&g_basic0_ctrl, &usb_event);
/* USB event received by R_USB_EventGet */
if (usb_event == USB_STATUS_REQUEST)
{
R_USB_SetupGet (&g_basic0_ctrl, &usb_setup);
if (USB_PCDC_SET_LINE_CODING == (usb_setup.request_type & USB_BREQUEST))
{
/* Configure virtual UART settings */
R_USB_PeriControlDataGet (&g_basic0_ctrl, (uint8_t*) &g_line_coding, LINE_CODING_LENGTH);
}
else if (USB_PCDC_GET_LINE_CODING == (usb_setup.request_type & USB_BREQUEST))
{
/* Send virtual UART settings back to host */
R_USB_PeriControlDataSet (&g_basic0_ctrl, (uint8_t*) &g_line_coding, LINE_CODING_LENGTH);
}
else if (USB_PCDC_SET_CONTROL_LINE_STATE == (usb_setup.request_type & USB_BREQUEST))
{
/* Acknowledge all other status requests */
R_USB_PeriControlStatusSet (&g_basic0_ctrl,
USB_SETUP_STATUS_ACK);
}
else
{
}
}
if(Rx_flag_finish == 1){
Rx_flag_finish = 0;
printf("%s", DataBuff);
if (memcmp(DataBuff, "LED on\r\n", 8) == 0){
strcpy (send_str, "LED is on\r\n");
led_level = BSP_IO_LEVEL_HIGH;
g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, led_level);
R_USB_Write(&g_basic0_ctrl, (uint8_t*) send_str, ((uint32_t) strlen(send_str)), (uint8_t) g_usb_class_type);
}
else if(memcmp(DataBuff, "LED off\r\n", 8) == 0){
strcpy (send_str, "LED is off\r\n");
led_level = BSP_IO_LEVEL_LOW;
g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, led_level);
R_USB_Write(&g_basic0_ctrl, (uint8_t*) send_str, ((uint32_t) strlen(send_str)), (uint8_t) g_usb_class_type);
}
printf_usart();
}
// g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, BSP_IO_LEVEL_LOW);
// R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
// g_ioport.p_api->pinWrite(&g_ioport_ctrl, User_LED, BSP_IO_LEVEL_HIGH);
// R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
/*******************************************************************************************************************//**
* This function is called at various points during the startup process. This implementation uses the event that is
* called right before main() to set up the pins.
*
* @param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart(bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
/* Enable reading from data flash. */
R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and
* C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */
#endif
}
if (BSP_WARM_START_POST_C == event)
{
/* C runtime environment and system clocks are setup. */
/* Configure pins. */
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
}
}
#if BSP_TZ_SECURE_BUILD
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();
/* Trustzone Secure Projects require at least one nonsecure callable function in order to build (Remove this if it is not required to build). */
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ()
{
}
#endif
usart_printf.c
* usart.c
*
* Created on: 2022年6月13日
* Author: huang
*/
#include <stdio.h>
#include "hal_data.h"
#include "usart_printf.h"
uint8_t RxBuff[1]; //进入中断接收数据的数组
uint8_t DataBuff[500]; //保存接收到的数据的数组
int RxLine=0; //接收到的数据长度
int Rx_flag=0; //接受到数据标志
int Rx_flag_finish=0; //接受完成
fsp_err_t err = FSP_SUCCESS;
volatile bool uart_send_complete_flag = false;
void user_uart_callback (uart_callback_args_t * p_args)
{
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
if(p_args->event == UART_EVENT_RX_CHAR)
{
RxBuff[0] = (uint8_t)p_args->data;
RxLine++; //每接收到一个数据,进入回调数据长度加1
DataBuff[RxLine-1]=RxBuff[0]; //把每次接收到的数据保存到缓存数组
Rx_flag=1;
if(RxBuff[0]=='\n') //接收结束标志位,这个数据可以自定义,根据实际需求,这里只做示例使用,不一定是0xff
{
Rx_flag_finish=1;
}
RxBuff[0]=0;
}
}
#ifdef __GNUC__ //串口重定向
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
err = R_SCI_UART_Write(&g_uart0_ctrl, (uint8_t *)&ch, 1);
if(FSP_SUCCESS != err) __BKPT();
while(uart_send_complete_flag == false){}
uart_send_complete_flag = false;
return ch;
}
int _write(int fd,char *pBuffer,int size)
{
for(int i=0;i<size;i++)
{
__io_putchar(*pBuffer++);
}
return size;
}
void printf_usart(void)
{
printf("length=%d\r\n",RxLine);
for(int i=0;i<RxLine;i++)
printf("data:[%d] = 0x%x\r\n",i,DataBuff);
memset(DataBuff,0,sizeof(DataBuff)); //清空缓存数组
//memset()作用:可以方便的清空一个结构类型的变量或数组。
//例句:memset(aTxbuffer,0,sizeof(aTxbuffer)) 用memset清空aTxbuffer。
RxLine=0; //清空接收长度
Rx_flag_finish=0;
Rx_flag = 0;
}
usart_printf.h
* usart.h
*
* Created on: 2022年6月13日
* Author: huang
*/
#ifndef INC_USART_H_
#define INC_USART_H_
extern int Rx_flag_finish; //接受完成
extern uint8_t DataBuff[500];
void printf_usart(void);
void user_uart_callback (uart_callback_args_t * p_args);
#endif /* INC_USART_H_ */
Then there is another experimental effect video. VID_20220701_001922(0)
|