RZ/G2L串口SCI的使用

cathy的头像
cathy 发布于:周四, 01/18/2024 - 16:33 ,关键词:

RZ/G2L串口简介

瑞萨RZ/G2L的串口简称SCI,全称Serial Communication Interface。

RZ/G2L有两种串口,一种带FIFO叫SCIFA,另一种不带FIFO叫SCIg。

1.png

所以,RZ/G2L有5路SCIFA和2路SCIg总共7路的串口。

RZ/G2L串口驱动

包含驱动代码和设备树两部分:

内核中的驱动文件sh-sci.c

该源码文件路径:drivers/tty/serial/sh-sci.c,该驱动代码包含SCIFA和SCIg两种型号的完整驱动代码。

编译内核的时候需进行以下配置

egrep -rin "CONFIG_SERIAL_SH_SCI" .out/.config
2180:CONFIG_SERIAL_SH_SCI=y
2181:CONFIG_SERIAL_SH_SCI_NR_UARTS=18
2182:CONFIG_SERIAL_SH_SCI_CONSOLE=y
2183:CONFIG_SERIAL_SH_SCI_EARLYCON=y
2184:CONFIG_SERIAL_SH_SCI_DMA=y

该配置能够确保sh-sci.c的驱动代码能够被编译包含到Image中。

github上提供的内核源码的defconfig中默认已包含SCI驱动代码的编译。

SCI串口的设备树节点在r9a07g044.dtsi中定义,分别定义了scif0~4, sci0~1共7路。

scif0: serial@1004b800 
{      
    compatible = "renesas,scif-r9a07g044";      
    reg = <0 0x1004b800 0 0x400>;      
    interrupts = <GIC_SPI 380 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 382 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 383 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 381 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi",            
                 "bri", "dri", "tei";      
    clocks = <&cpg CPG_MOD R9A07G044_SCIF0_CLK_PCK>;      
    clock-names = "fck";      dmas = <&dmac 0x4e79>, <&dmac 0x4e7a>;      
    dma-names = "tx", "rx";      power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCIF0_RST_SYSTEM_N>;      
    status = "disabled";    
};

scif1: serial@1004bc00 
{      
    compatible = "renesas,scif-r9a07g044";      
    reg = <0 0x1004bc00 0 0x400>;      
    interrupts = <GIC_SPI 385 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 387 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 388 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 386 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 389 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 389 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi",            
                 "bri", "dri", "tei";      
    clocks = <&cpg CPG_MOD R9A07G044_SCIF1_CLK_PCK>;      
    clock-names = "fck";      
    dmas = <&dmac 0x4e7d>, <&dmac 0x4e7e>;      
    dma-names = "tx", "rx";      
    power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCIF1_RST_SYSTEM_N>;      
    status = "disabled";    
};
    
scif2: serial@1004c000 
{      
    compatible = "renesas,scif-r9a07g044";      
    reg = <0 0x1004c000 0 0x400>;      
    interrupts = <GIC_SPI 390 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 391 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi",            
                 "bri", "dri", "tei";     
     clocks = <&cpg CPG_MOD R9A07G044_SCIF2_CLK_PCK>;      
     clock-names = "fck";      
     dmas = <&dmac 0x4e81>, <&dmac 0x4e82>;      
     dma-names = "tx", "rx";      
     power-domains = <&cpg>;      
     resets = <&cpg R9A07G044_SCIF2_RST_SYSTEM_N>;      
     status = "disabled";    
 };
    
scif3: serial@1004c400 
{      
    compatible = "renesas,scif-r9a07g044";      
    reg = <0 0x1004c400 0 0x400>;      
    interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi",           
                  "bri", "dri", "tei";     
    clocks = <&cpg CPG_MOD R9A07G044_SCIF3_CLK_PCK>;      
    clock-names = "fck";      
    dmas = <&dmac 0x4e85>, <&dmac 0x4e86>;      
    dma-names = "tx", "rx";      
    power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCIF3_RST_SYSTEM_N>;      
    status = "disabled";    
};
    
scif4: serial@1004c800 
{      
    compatible = "renesas,scif-r9a07g044";      
    reg = <0 0x1004c800 0 0x400>;      
    interrupts = <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi",            
                 "bri", "dri", "tei";      
    clocks = <&cpg CPG_MOD R9A07G044_SCIF4_CLK_PCK>;      
    clock-names = "fck";      
    dmas = <&dmac 0x4e89>, <&dmac 0x4e8a>;      
    dma-names = "tx", "rx";      
    power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCIF4_RST_SYSTEM_N>;      
    status = "disabled";    
};
    
sci0: serial@1004d000 
{      
    compatible = "renesas,r9a07g044-sci", "renesas,rz-sci";      
    reg = <0 0x1004d000 0 0x400>;      
    interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 406 IRQ_TYPE_EDGE_RISING>,             
                 <GIC_SPI 407 IRQ_TYPE_EDGE_RISING>,             
                 <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi", "tei";      
    clocks = <&cpg CPG_MOD R9A07G044_SCI0_CLKP>;      
    clock-names = "fck";      
    power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCI0_RST>;      
    status = "disabled";    
};
    
sci1: serial@1004d400 
{      
    compatible = "renesas,r9a07g044-sci", "renesas,rz-sci";      
    reg = <0 0x1004d400 0 0x400>;      
    interrupts = <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,             
                 <GIC_SPI 410 IRQ_TYPE_EDGE_RISING>,            
                 <GIC_SPI 411 IRQ_TYPE_EDGE_RISING>,             
                 <GIC_SPI 412 IRQ_TYPE_LEVEL_HIGH>;      
    interrupt-names = "eri", "rxi", "txi", "tei";      
    clocks = <&cpg CPG_MOD R9A07G044_SCI1_CLKP>;      
    clock-names = "fck";      
    power-domains = <&cpg>;      
    resets = <&cpg R9A07G044_SCI1_RST>;      
    status = "disabled";    
};

设备树文件路径

arch/arm64/boot/dts/renesas/r9a07g044.dtsi

根据项目需要,使能需要使用的设备节点,如欲使用SCIFA2:

&scif2 
{  
    pinctrl-0 = <&scif2_pins>;  
    pinctrl-names = "default";  
    uart-has-rtscts;  
    status = "okay";
};

注意需通过scif2_pins正确处理IO口复用问题。如果不使用DMA需要将设备节点中的dmas和dma-names删除。

编译内核:

export ARCH=arm64
export CROSS_COMPILE=aarch64-none-elf-
export PATH=$PATH:/opt/arm/gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf/bin
make defconfig O=.out && make -j8 O=.out

使用当前编译生成的内核Image和dtb

.out/arch/arm64/boot/Image 
.out/arch/arm64/boot/dts/renesas/r9a07g044l2-smarc.dtb

启动板子后就能在系统路径下生成/dev/ttySC2节点。

Linux应用层使用RZ/G2L的串口SCI

RZ/G2L的串口设备节点在Linux应用层遵循POSIX标准,使用方法和PC端的Ubuntu系统并无差别。

RZ/G2L除了支持市面上常用的串口波特率如9600/115200/921600等,实际上除POSIX系统定义的波特率都支持外,RZ/G2L能够支持的最大串口波特率是12.5Mbps,下一篇我们将介绍如何在内核驱动代码中实现RZ/G2L的最大波特率12.5Mbps。

如需了解更详细的使用方法请参考如下网站:

1、瑞萨官网

https://www.renesas.cn/cn/zh/products/microcontrollers-microprocessors/rz-mpus/rzg2l-getting-started 

2、RZ产品WIKI网站

https://renesas.info/wiki/Main_Page 

您可点击下方网址进入瑞萨中文论坛查看:

https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/ 

RZ/G2L支持的最大波特率

2.png

RZ/G2L的SCIFA异步通讯模式下支持的最高波特率可以达到12.5Mbps,如果异步基础时钟选择16倍波特率,同时关闭波特率发生器的倍频模式下依然可以达到3.125Mbps。如果异步基础时钟选择8倍波特率或者波特率发生器开启倍频模式,最大波特率可以达到6.25Mbps。

在上集中我们有讲过RZ/G2L在Linux下的使用遵循POSIX标准。只要POSIX支持的波特率,RZ/G2L都可以支持,并且支持各种波特率下的误差修正,需要开启MDDRS寄存器。

Linux下串口的波特率

Linux下termbits.h支持的波特率如下

/* c_cflag bit meaning */
#define CBAUD   0000377
#define  B0 0000000     /* hang up */
#define  B50    0000001#define  B75    0000002
#define  B110   0000003#define  B134   0000004
#define  B150   0000005#define  B200   0000006
#define  B300   0000007#define  B600   0000010
#define  B1200  0000011#define  B1800  0000012
#define  B2400  0000013#define  B4800  0000014
#define  B9600  0000015#define  B19200 0000016
#define  B38400 0000017#define  EXTA   B19200
#define  EXTB   B38400#define  CBAUDEX 0000000
#define  B57600   00020#define  B115200  00021
#define  B230400  00022#define  B460800  00023
#define  B500000  00024#define  B576000  00025
#define  B921600  00026#define B1000000  00027
#define B1152000  00030#define B1500000  00031
#define B2000000  00032#define B2500000  00033
#define B3000000  00034#define B3500000  00035
#define B4000000  00036

也就是标准的Linux支持的最大波特率是4Mbps,但并不是4Mbps以下任意一个波特率都可以支持,只有30种选择。

那如果在特殊的应用场景中,需要这30种波特率以外的选择,是否能够实现呢?答案是肯定的,但是比较复杂。

这里我们提供一种Linux下实现非POSIX标准串口波特率的方法给大家参考。

Linux串口非标波特率的实现

涉及两部分他,包括内核和应用层

首先第一步:我们需要修改内核中的串口驱动,确保串口驱动能够支持需要添加的非标波特率。上集我们已经分享过RZ/G2L的串口驱动代码路径是drivers/tty/serial/sh-sci.c,目前通过开启MDDRS,RZ/G2L几乎可以支持12.5Mbps以下的任意串口波特率。

这里我们以前面提到的3.125Mbps/6.25Mbps/12.5Mbps为例,github上下载的sh-sci.c驱动默认并没有开启波特率发生器的倍频模式,异步基础时钟选择的是默认的16倍波特率。所以最大的波特率可以支持到3.125Mbps,如果需要支持6.25Mbps或者更高的12.5Mbps,需要开启波特率发生器的倍频模式,并且允许异步基础时钟选择8倍波特率。

+       #if ABCS0_BGDM_EN
+           if(baud > 6250000){
+               //SEMR_BGDM:Baud rate generator double-speed mode Select:
+               //SEMR_ABCS0:Asynchronous Base Clock Select:
+               serial_port_out(port, SEMR,
+                       serial_port_in(port, SEMR) | (SEMR_ABCS0 | SEMR_BGDM));
+               freq *= 2;
+               prediv /= 2;
+           }else if(baud > 3125000){
+               //SEMR_BGDM:Baud rate generator double-speed mode Select:
+               serial_port_out(port, SEMR,
+                   serial_port_in(port, SEMR) | SEMR_BGDM);
+               freq *= 2;
+           }
+       #endif

这部分代码与RZ/G2L的平台相关,需要根据RZ/G2L的规格书配置对应的寄存器。

第二步:为了允许应用层配置我们添加的这三种波特率,需要修改drivers/tty/tty_baudrate.c和include/uapi/asm-generic/termbits.h,这两个文件与平台无关。想要在内核中添加系统默认的30种波特率以外的波特率都需要修改这两个文件。这两个文件的修改内容可以参考以下:

diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c
index bdfaee2c1331..75d287893d11 100644
--- a/drivers/tty/tty_baudrate.c
+++ b/drivers/tty/tty_baudrate.c
@@ -24,7 +24,7 @@ static const speed_t baud_table[] = {    
    1000000, 1152000, 1500000, 2000000 
#else    
    500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
-   2500000, 3000000, 3500000, 4000000
+   2500000, 3000000, 3500000, 4000000, 3125000, 6250000, 12500000 
  #endif 
}; 

@@ -36,7 +36,7 @@ static const tcflag_t baud_bits[] = {    
    B1000000, B1152000, B1500000, B2000000 
#else    
    B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000,
-   B2500000, B3000000, B3500000, B4000000
+   B2500000, B3000000, B3500000, B4000000, B3125000, B6250000, B12500000 
#endif 
};
 
@@ -73,6 +73,14 @@ speed_t tty_termios_baud_rate(struct ktermios *termios)        
    else            
    cbaud += 15;    
    }
+   if (cbaud & CBAUDEX2) {
+       cbaud &= ~CBAUDEX2;
+
+       if (cbaud < 1 || cbaud + 30 > n_baud_table)
+           termios->c_cflag &= ~CBAUDEX2;
+       else
+           cbaud += 30;
+   }    
    return cbaud >= n_baud_table ? 0 : baud_table[cbaud]; 
} 
  EXPORT_SYMBOL(tty_termios_baud_rate);
diff --git a/include/uapi/asm-generic/termbits.h b/include/uapi/asm-generic/termbits.h
index 7db62a33ee52..1353300b6934 100644
--- a/include/uapi/asm-generic/termbits.h
+++ b/include/uapi/asm-generic/termbits.h
@@ -110,7 +110,7 @@ struct ktermios { 
  #define   FF1  0100000  
  
/* c_cflag bit meaning */
-#define CBAUD  0010017
+#define CBAUD  0030017 
 #define  B0    0000000     /* hang up */ 
 #define  B50   0000001 
 #define  B75   0000002

@@ -158,7 +158,9 @@ struct ktermios { 
 #define  B3500000 0010016 
 #define  B4000000 0010017
+#define CBAUDEX2 0020000
+#define  B3125000  0020001
+#define  B6250000  0020002
+#define  B12500000 0020003 
 #define CIBAUD   002003600000  /* input baud rate */ 
 #define CMSPAR   010000000000  /* mark or space (stick) parity */ 
 #define CRTSCTS      020000000000  /* flow control */

经过上面两步修改,内核已支持我们需要添加的3种非POSIX标准的串口波特率。

接下来演示应用层如何使用我们添加的这三种串口波特率。

#define B3125000  0020001
#define B6250000  0020002
#define B12500000 0020003
**
** 串口配置
** 参数 cfg 指向一个 uart_cfg_t 结构体对象
**/
static int uart_cfg(const uart_cfg_t *cfg)
{    
    struct termios new_cfg = {0}; //将 new_cfg 对象清零    
    speed_t speed;        
    
    /* 设置为原始模式 */    
    cfmakeraw(&new_cfg);        
    
    /* 使能接收 */    
    new_cfg.c_cflag |= CREAD| CLOCAL;        
    
    /* 设置波特率 */  
    speed = B3125000; // B3125000  B6250000  B12500000
    new_cfg.c_cflag |= speed;
    
    /* 串口的其他属性配置参考标准的POSIX */
    
    /* 写入配置、使配置生效 */    
    if (0 > tcsetattr(fd, TCSANOW, &new_cfg)) 
    {        
        fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));        
        return -1;    
    }

经过以上修改,我们就可以在linux下使用文章开头提到的RZ/G2L的最大波特率12.5Mbps进行串口通讯。

需要注意的是,我们给RZ/G2L添加的这三个波特率尤其是6.25Mbps或者12.5Mbps远超标准linux下支持的最大波特率4Mbps,所以,通过PC端的Ubuntu是无法使用这三种波特率与RZ/G2L的SMARC EVK板进行通讯测试的,如果要使用我们上面添加的这三种波特率,只能在两个SMARC EVK板上进行。

所以,除以上添加的这三种波特率外,如果要添加POSIX标准支持的30种以外的其他波特率,都可以参考这个方法来实现。

如需了解更详细的使用方法请参考如下网站:

1、瑞萨官网

https://www.renesas.cn/cn/zh/products/microcontrollers-microprocessors/rz-mpus/rzg2l-getting-started 

2、RZ产品WIKI网站

https://renesas.info/wiki/Main_Page 

您可点击下方网址进入瑞萨中文论坛查看:

https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/ 

来源:瑞萨嵌入式小百科:RZ/G2L串口SCI的使用(上)RZ/G2L串口SCI的使用(下)

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 160