注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
黄吴久的个人空间 https://home.eeworld.com.cn/space-uid-1083629.html [收藏] [复制] [分享] [RSS]
日志

【RTT&瑞萨高性能 CPK-RA6M4 开发板 测评】- 串口和USB通信

热度 1已有 1242 次阅读2022-7-1 00:27 |个人分类:瑞萨

这篇接着讲讲串口和USB通信,主要讲讲串口和USB-CDC类的配置,对于串口和USB-CDC的使用就简单说两句。

首先是串口的配置,在打开图形化配置工具,在Pins设置中找到Peripherals->SCI,然后选中SCI0-SCI9其中一个,就可以在Pin Configuration中配置Operation Mode为Asynchronous UART模式了,也就是我们常用的全双工串口,然后可以配置TXD0和RXD0的引脚分配

 

我 选择的是P101和P410这两个脚,这两个脚在评估版上的位置是挨着的,如下图

 

选择好要使用的引脚后,我们就要给串口分配堆栈了,打开Stacks页面

点击1.New Stack,打开一个下拉列表,在其中找到Connectivity->UART(r_sci_uart),点击后就能生成2中的堆栈。

   然后点击右上角的1处,将透视图切换到图形化配置工具界面,然后点击我们刚添加的堆栈模块,就能看到左下角这个属性配置界面。不得不吐槽下,这个编辑器的透视图切换真的不智能,都不能自己切换配置界面,不知道是不是只有我的这样。

对于这个属性的配置,暂时只用惯性下图中红框内内容就行了,1中的内容可以不用更改,2中的第一项为波特率,可以根据具体情况而定,3中的Callback后是串口回调函数的名称,写一个好识别的就行,比如我写的是user_uart_callback,到这串口的配置就算完成了。

然后来讲讲USB-CDC的配置,

还是一样,第一步先进行USB的时钟配置,打开Clock页面,下图中的箭头流向标注的USB时钟的来源,我们将PLL2 Src改为: HOCO,改变PLL2 Div和PLL2 Mul的值,将UCLK Src选为PLL2,改变UCLK Div的值,最终使USB的时钟UCLK为48MHz。

 

然后是管脚配置,找到下图中的USB,将Operation Mode选择为Device,驱动模式。

 

然后切换到Stack界面,添加USB_PCDC(r_usb_pcdc)

点击新创建的堆栈模块,在属性中可以看到关于USB的相关配置,这里不做修改。

然后就点击右上角的运行图标来获取更新代码。

串口和USB都配置完了,那么下面就讲讲代码,这此我需要实现的是通过串口发送命令给单片机,单片机点亮或熄灭LED,并通过USB将LED状态发送到虚拟串口上。

首先是串口,对于串口我们需要知道下面这三个函数,分别是开启串口,串口读取数据,串口发送数据。

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()这个函数需要在程序运行时使用,来启用串口,我将它放在hal_entry()函数开始的位置。

对于串口的接收和发送部分我也是在网上找到的,建议可以去看看这篇博客。在这篇博客中有讲如何重定向printf函数和串口如何收发数据。

对于我来说,我暂时只接收一定长度的数据,我将我的串口处理部分先贴出来,

在串口文件usart_printf.c中串口处理部分:

#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);
    memset(DataBuff,0,sizeof(DataBuff));  //清空缓存数组
    //memset()作用:可以方便的清空一个结构类型的变量或数组。
    //例句:memset(aTxbuffer,0,sizeof(aTxbuffer))  用memset清空aTxbuffer。
    RxLine=0;  //清空接收长度
    Rx_flag_finish=0;
    Rx_flag = 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;
}

串口部分的代码基本和参考的那篇博客中的一样,在user_uart_callback (uart_callback_args_t * p_args),这个函数中,我将串口接收结尾标志改为'\n',这样就可以接受字符串了。

对于USB-CDC部分的代码参考了瑞萨的使用手册。

需要创建四个变量:

/* 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类

然后是USB的初始化:

//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);

由于没有使用回调函数,这里就在主函数中循环读取USB状态:

        /* 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
            {
            }
        }

在解析状态中可以获取到usb读到数据的状态,可以在USB接收数据是使用。我暂时不用USB接收数据,只用USB发送数据,相对简单一点。

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)

上面这个函数是USB发送数据时使用的。USB发送数据如下面所示:

        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();
        }

在上面的代码中,我实现了对串口数据的对比,识别到控制命令“LED on\r\n”和“LED off\r\n”,然后对LED状态进行改变,并且通过USB将LED状态发送到虚拟串口。

完整代码包含三个文档:

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_ */

然后又一个实验效果视频。

VID_20220701_001922(0)

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章