【正点原子Linux连载】第三十二章 Linux RS232/485驱动实验 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三十二章 Linux RS232/485驱动实验

串口是很常用的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信,根据电平的不同,串口分为TTL和RS232。不管是什么样的接口电平,其驱动程序都是一样的,通过外接RS485这样的芯片就可以将串口转换为RS485信号,正点原子的ATK-DLRK3568开发板就是这么做的。
RS232和RS485接口分别连接到了ATK-DLRK3568的UART3和UART4接口上。本章我们就来学习一下如何驱动ATK-DLRK3568开发板上的UART3和UART4,进而来实现RS232和RS485的实验。

32.1 Linux下UART驱动框架
1、uart_driver注册和注销
与前面的I2C一样,Linux也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱动程序即可。串口驱动没什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也已经由瑞芯微官方编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttySx文件,其中x代表数字。
虽然串口驱动不需要我们去写,但是串口驱动框架我们还是要了解,uart_driver结构体代表UART驱动,uart_driver定义在include/linux/serial_core.h文件中,内容如下:
示例代码32.1.1 uart_driver结构体

1 struct uart_driver {
2   struct module       *owner;     //模块所属者
3   const char      *driver_name;   //驱动名字
4   const char      *dev_name;      //设备名字
5   int          major;             //主设备号
6   int          minor;             //次设备号
7   int          nr;                //控制数
8   struct console      *cons;      //控制台
9
10  /*
11   * these are private; the low level driver should not
12   * touch these; they should be initialised to NULL
13   */
14  struct uart_state   *state;
15  struct tty_driver   *tty_driver;
16};

每个串口驱动需要定义一个uart_driver,加载驱动的时候通过uart_register_driver函数向系统注册这个uart_driver,函数原型如下:
int uart_register_driver(struct uart_driver *uart)
函数参数和返回值含义如下:
uart:要注册的uart_driver。
返回值:0,成功;负值,失败。
注销驱动的时候也需要注销掉前面注册的uart_driver,需要用到uart_unregister_driver函数原型如下:
void uart_unregister_driver(struct uart_driver *uart)
函数参数和返回值含义如下:
uart:要注销的uart_driver。
返回值:无
2、uart_port的添加与移除
uart_port表示一个具体的port,uart_port定义在include/linux/serial_core.h文件,内容如下(有省略):
示例代码32.1.2 uart_port结构体

1 struct uart_port {
2     spinlock_t      	lock;           		/* port lock 		*/
3     unsigned long    	iobase;         		/* in/out[bwl] 	*/
4     unsigned char 	__iomem   *membase; 	/* read/write[bwl] */
......
134     const struct 		uart_ops   *ops;
135     unsigned int     	custom_divisor;
136     unsigned int     	line;           		/* port index 		*/
137     unsigned int     	minor;
138     resource_size_t 	mapbase;        		/* for ioremap 	*/
139     resource_size_t 	mapsize;
140     struct device    	*dev;           		/* parent device 	*/
......
149 };

uart_port中最主要的就是134行的ops,ops包括了串口的具体驱动函数,这个我们稍后再看。每个UART都有一个uart_port,那么uart_port是怎么和uart_driver结合起来的呢。这里要用到uart_add_one_port函数,函数原型如下:
int uart_add_one_port(stuct uart_driver *reg, stuct uart_port *port)
函数参数和返回值含义如下:
reg:要注册的串口驱动程序。
prot:要注册的串口端口。
返回值:0,成功;负值,失败。
卸载UART驱动的时候也需要将uart_port从相应的uart_driver中移除,需要用到uart_remove_one_port函数,函数原型如下:
int uart_remove_one_port(struct uart_driver *reg, struct uart_prot *prot)
reg:要卸载程序。
prot:要卸载口端口。
返回值:0,成功;负值,失败。
3、uart_ops实现
在上面讲解uart_port的时候说过,uart_port中的pos成员变量很重要,因为ops包含了针对UART具体的驱动函数,Linux系统收发数据最终调用的都是ops中的函数。ops是uart_ops类型的结构体指针变量,uart_ops定义在include/linux/serial_core.h文件中,内容如下:
示例代码32.1.3 uart_poat结构体

1 struct uart_ops {
2   unsigned int    (*tx_empty)(struct uart_port *);
3   void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);
4   unsigned int    (*get_mctrl)(struct uart_port *);
5   void        (*stop_tx)(struct uart_port *);
6   void        (*start_tx)(struct uart_port *);
7   void        (*throttle)(struct uart_port *);
8   void        (*unthrottle)(struct uart_port *);
9   void        (*send_xchar)(struct uart_port *, char ch);
10  void        (*stop_rx)(struct uart_port *);
11  void        (*enable_ms)(struct uart_port *);
12  void        (*break_ctl)(struct uart_port *, int ctl);
13  int     (*startup)(struct uart_port *);
14  void        (*shutdown)(struct uart_port *);
15  void        (*flush_buffer)(struct uart_port *);
16  void        (*set_termios)(struct uart_port *, struct ktermios *new,
17                     struct ktermios *old);
18  void        (*set_ldisc)(struct uart_port *, struct ktermios *);
19  void        (*pm)(struct uart_port *, unsigned int state,
20                unsigned int oldstate);
21
22  /*
23   * Return a string describing the type of the port
24   */
25  const char  *(*type)(struct uart_port *);
26
27  /*
28   * Release IO and memory resources used by the port.
29   * This includes iounmap if necessary.
30   */
31  void        (*release_port)(struct uart_port *);
32
33  /*
34   * Request IO and memory resources used by the port.
35   * This includes iomapping the port if necessary.
36   */
37  int     (*request_port)(struct uart_port *);
38  void        (*config_port)(struct uart_port *, int);
39  int     (*verify_port)(struct uart_port *, struct serial_struct *);
40  int     (*ioctl)(struct uart_port *, unsigned int, unsigned long);
41#ifdef CONFIG_CONSOLE_POLL
42  int     (*poll_init)(struct uart_port *);
43  void        (*poll_put_char)(struct uart_port *, unsigned char);
44  int     (*poll_get_char)(struct uart_port *);
45#endif
46};

UATT驱动编写人员需要实现uart_ops,因为uart_ops是最底层的UART驱动接口,是实实在在的和UART寄存器打交道的。关于uart_ops结构体中的这些函数的具体含义请参考Documentation/serial/driver这个文档。
UART驱动框架大概就是这些,接下来我们理论联系实际,看一下瑞芯微官方的UART驱动文件是如何编写的。
32.2 RK3568 UART驱动分析
1、8250串口通用驱动文件
在瑞芯微官方提供的内核可以得知,使用了8250串口通用驱动,以下为主要驱动文件

drivers/tty/serial/8250/8250_core.c 	# 8250串口驱动核心
drivers/tty/serial/8250/8250_dw.c 		# Synopsis DesignWare 8250串口驱动
drivers/tty/serial/8250/8250_dma.c 	# 8250串口DMA驱动
drivers/tty/serial/8250/8250_port.c 	# 8250串口端口操作
drivers/tty/serial/8250/8250_early.c 	# 8250串口early console驱动
2、UART的platform驱动框架

下面我们以UART3来分析,打arch/arm64/boot/dts/rockchip/rk3568.dtsi文件,找到UART3对应的子节点,子节点内容如下所示:
示例代码32.2.1 uart3设备节点

1   uart3: serial@fe670000 {
2       compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";
3       reg = <0x0 0xfe670000 0x0 0x100>;
4       interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
5       clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
6       clock-names = "baudclk", "apb_pclk";
7       reg-shift = <2>;
8       reg-io-width = <4>;
9       dmas = <&dmac0 6>, <&dmac0 7>;
10      pinctrl-names = "default";
11      pinctrl-0 = <&uart3m0_xfer>;
12      status = "disabled";
13  };
重点看一下第2行compatible属性值为“snps,dw-apb-uart”。在Linux源码中搜索这个值即可找到对应的UART驱动文件,此文件为drivers/tty/serial/8250/8250_dw.c,在此文件中可以找到如下内容:

示例代码32.2.2 UART platform驱动框架

1 static const struct of_device_id dw8250_of_match[] = {
2   { .compatible = "snps,dw-apb-uart" },
3   { .compatible = "cavium,octeon-3860-uart" },
4   { .compatible = "marvell,armada-38x-uart" },
5   { .compatible = "renesas,rzn1-uart" },
6   { /* Sentinel */ }
7 };
8  MODULE_DEVICE_TABLE(of, dw8250_of_match);
.......
24 MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
25
26 static struct platform_driver dw8250_platform_driver = {
27  .driver = {
28      .name       = "dw-apb-uart",
29      .pm     = &dw8250_pm_ops,
30      .of_match_table = dw8250_of_match,
31      .acpi_match_table = dw8250_acpi_match,
32  },
33  .probe          = dw8250_probe,
34  .remove         = dw8250_remove,
35};
36
37 module_platform_driver(dw8250_platform_driver);

可以看出瑞芯微的UART本质上是一个platform驱动,第1~7行,设备树所使用的匹配表,第2行的compatible属性值为“snps,dw-apb-uart”。
第25~35行,platform驱动框架结构体dw8250_platform_driver。
接下来,我们继续看文件drivers/tty/serial/8250/8250_core.c。
示例代码32.2.3 驱动的初始化

1   static int __init serial8250_init(void)
2   {
3       int ret;
4 
5       if (nr_uarts == 0)
6           return -ENODEV;
7 
8       serial8250_isa_init_ports();
9 
10  pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %sabled\n",
11          nr_uarts, share_irqs ? "en" : "dis");
12
13  #ifdef CONFIG_SPARC
14      ret = sunserial_register_minors(&serial8250_reg, UART_NR);
15  #else
16      serial8250_reg.nr = UART_NR;
17      ret = uart_register_driver(&serial8250_reg);
18  #endif
19      if (ret)
20          goto out;
21
22      ret = serial8250_pnp_init();
23      if (ret)
24          goto unreg_uart_drv;
25
26      serial8250_isa_devs = platform_device_alloc("serial8250",
27                              PLAT8250_DEV_LEGACY);
28      if (!serial8250_isa_devs) {
29          ret = -ENOMEM;
30          goto unreg_pnp;
31      }
32
33      ret = platform_device_add(serial8250_isa_devs);
34      if (ret)
35          goto put_dev;
36
37     serial8250_register_ports(&serial8250_reg, 
38										&serial8250_isa_devs->dev);
39      ret = platform_driver_register(&serial8250_isa_driver);
40      if (ret == 0)
41          goto out;
42
43      platform_device_del(serial8250_isa_devs);
44  put_dev:
45      platform_device_put(serial8250_isa_devs);
46  unreg_pnp:
47      serial8250_pnp_exit();
48  unreg_uart_drv:
49  #ifdef CONFIG_SPARC
50      sunserial_unregister_minors(&serial8250_reg, UART_NR);
51  #else
52      uart_unregister_driver(&serial8250_reg);
53  #endif
54  out:
55      return ret;
56  }
57
58  static void __exit serial8250_exit(void)
59  {
60      struct platform_device *isa_dev = serial8250_isa_devs;
61
62      /*
63       * This tells serial8250_unregister_port() not to re-register
64       * the ports (thereby making serial8250_isa_driver permanently
65       * in use.)
66       */
67      serial8250_isa_devs = NULL;
68
69      platform_driver_unregister(&serial8250_isa_driver);
70      platform_device_unregister(isa_dev);
71
72      serial8250_pnp_exit();
73
74  #ifdef CONFIG_SPARC
75      sunserial_unregister_minors(&serial8250_reg, UART_NR);
76  #else
77      uart_unregister_driver(&serial8250_reg);
78  #endif
79  }
80
81  #ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
82  rootfs_initcall(serial8250_init);
83  #else
84  module_init(serial8250_init);
85  #endif
86  module_exit(serial8250_exit);
第1~56行,驱动入口函数,第8行此函数用于初始化串口端口的ISA总线驱动程序,主要实现请求端口、添加和注册串口端口,第17行调用uart_register_driver函数向Linux内核注册uart_driver,在这里就是serial8250_reg。
第58~79行,驱动出口函数,第77行调用uart_unregister_driver函数注销掉前面注册的uart_driver,在这里就是serial8250_reg。
3、uart_driver初始化
在serial8250_init函数中向Linux内核注册了serial8250_reg,serial8250_reg就是uart_driver类型的结构体变量,serial8250_reg定于如下:

示例代码32.2.4 serial8250_reg结构体

1 static struct uart_driver serial8250_reg = {
2   .owner          = THIS_MODULE,
3   .driver_name        = "serial",
4   .dev_name       = "ttyS",
5   .major          = TTY_MAJOR,
6   .minor          = 64,
7   .cons           = SERIAL8250_CONSOLE,
8 };
4、uart_port初始化与添加

当UART设备和驱动匹配成功以后dw8250_probe函数就会执行,此函数重点就是初始uart_port,然后将其添加到对应的uart_driver中。接下来看一下dw8250_probe函数,函数内容如下所示:
示例代码32.2.5 dw8250_probe函数

1   static int dw8250_probe(struct platform_device *pdev)
2   {
3       struct uart_8250_port uart = {};
4       struct resource *regs = platform_get_resource(pdev, 
IORESOURCE_MEM, 0);
5       int irq = platform_get_irq(pdev, 0);
6       struct uart_port *p = &uart.port;
7       struct device *dev = &pdev->dev;
8       struct dw8250_data *data;
9       int err;
10      u32 val;
.......
23      spin_lock_init(&p->lock);
24      p->mapbase  = regs->start;
25      p->irq      = irq;
26      p->handle_irq   = dw8250_handle_irq;
27      p->pm       = dw8250_do_pm;
28      p->type     = PORT_8250;
29      p->flags    = UPF_SHARE_IRQ | UPF_FIXED_PORT;
30      p->dev      = dev;
31      p->iotype   = UPIO_MEM;
32      p->serial_in    = dw8250_serial_in;
33      p->serial_out   = dw8250_serial_out;
34      p->set_ldisc    = dw8250_set_ldisc;
35      p->set_termios  = dw8250_set_termios;
.......
135     data->rst = devm_reset_control_get_optional_exclusive(dev,														NULL);
136     if (IS_ERR(data->rst)) {
137         err = PTR_ERR(data->rst);
138         goto err_pclk;
139     }
140     reset_control_deassert(data->rst);
141
142     dw8250_quirks(p, data);
143
144     /* If the Busy Functionality is not implemented, don't handle it*/
145     if (data->uart_16550_compatible)
146         p->handle_irq = NULL;
147
148     if (!data->skip_autocfg)
149         dw8250_setup_port(p);
150
151     /* If we have a valid fifosize, try hooking up DMA */
152     if (p->fifosize) {
153         data->dma.rxconf.src_maxburst = p->fifosize / 4;
154         data->dma.txconf.dst_maxburst = p->fifosize / 4;
155         uart.dma = &data->dma;
156     }
157
158     data->line = serial8250_register_8250_port(&uart);
159     if (data->line < 0) {
160         err = data->line;
161         goto err_reset;
162     }
.......
187     return err;
188 }
第23行,初始化自旋锁函数
第142行,调用dw8250_quirks函数,它主要通过of_alias_get_id函数从设备树的aliases节点中获取“serial”相关的ID,以及获取对应的串口信息。
第158行,调用serial8250_register_8250_port函数,它主要负责向uart_driver添加uart_port,对应的就是在serial8250_reg添加uart->port。

5、serial8250_pops 结构体变量
serial8250_pops就是uart_ops类型的结构体变量,保存了RK3568串口最底层的操作函数,定义如下:
示例代码32.2.6 serial8250_pops

1   static const struct uart_ops serial8250_pops = {
2       .tx_empty   = serial8250_tx_empty,
3       .set_mctrl  = serial8250_set_mctrl,
4       .get_mctrl  = serial8250_get_mctrl,
5       .stop_tx    = serial8250_stop_tx,
6       .start_tx   = serial8250_start_tx,
7       .throttle   = serial8250_throttle,
8       .unthrottle = serial8250_unthrottle,
9       .stop_rx    = serial8250_stop_rx,
10      .enable_ms  = serial8250_enable_ms,
11      .break_ctl  = serial8250_break_ctl,
12      .startup    = serial8250_startup,
13      .shutdown   = serial8250_shutdown,
14      .set_termios    = serial8250_set_termios,
15      .set_ldisc  = serial8250_set_ldisc,
16      .pm     = serial8250_pm,
17      .type       = serial8250_type,
18      .release_port   = serial8250_release_port,
19      .request_port   = serial8250_request_port,
20      .config_port    = serial8250_config_port,
21      .verify_port    = serial8250_verify_port,
22  #ifdef CONFIG_CONSOLE_POLL
23      .poll_get_char = serial8250_get_poll_char,
24      .poll_put_char = serial8250_put_poll_char,
25  #endif
26  };
serial8250_pops中的函数基本都是和RK3568的UART寄存器打交道的,这里就不去详细的分析了。简单的了解了RK3568的UART驱动以后我们再来学习一下,如何驱动正点原子ATK-DLRK3568开发板上的UART3接口和UART4接口。

32.3 硬件原理图分析
本实验要用到ATK-DLRK3568的UART3接口和UART5接口,UART3连接RS232的母头,UART4连接RS485。我们依次来看一下这两个串口的硬件原理图。
1、RS232原理图
RS232原理图如图32.3.1所示:
在这里插入图片描述

图32.3.1 RS232原理图
COM1母头连接到UART3接口上,把JP6的1-3和2-4连接起来以后SP232就和UART3连接到一起了,UART3_TX_M1和UART3_RX_M1分别接到了GPIO3_B7和GPIO3_C0这两个引脚上。
2、RS485原理图
RS485原理图如图32.3.2所示:
在这里插入图片描述

图32.3.2 RS485原理图
RS485采用SP3485EN这款芯片来实现,RO为数据输出端,DI为数据接收端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。把JP2的1-3和2-4连接起来以后SP3485EN就和UART4连接到一起了,UART4_TX_M1和UART4_RX_M1分别接到了GPIO3_B2和GPIO3_B1这两个引脚上。在图32.3.2中RE和DE经过一系列的电路,最终通过RS485_RX来控制,这样我们可以省掉一个RS485收发控制IO,将RS485完全当作一个串口来使用,方便我们写驱动。
32.4 RS232驱动编写
前面我们已经说过了,RK3568的UART驱动RK厂商已经编写好了,所以不需要我们编写。我们要做的就是在设备树中添加UART3对应的设备节点即可。打开rk3568-atk-evb1-ddr4-v10.dtsi文件,因为uart3的节点在rk3568.dtsi已经存在了,我们只要在rk3568-atk-evb1-ddr4-v10.dtsi文件里面向这些节点追加一些内容即可,追加的步骤如下:
1、添加uart3的引脚信息
我们先在rk3568-pinctrl.dtsi文件下看有没有uart3的引脚配置,以及引脚配置是不是我开发板所对应的。默认情况下rk3568-pinctrl.dtsi里面是有uart3的引脚配置,如下图所示:
示例代码32.4.1 要追加的pinmux配置

1   uart3 {
2       /omit-if-no-ref/
3       uart3m0_xfer: uart3m0-xfer {
4           rockchip,pins =
5               /* uart3_rxm0 */
6               <1 RK_PA0 2 &pcfg_pull_up>,
7               /* uart3_txm0 */
8               <1 RK_PA1 2 &pcfg_pull_up>;
9       };
......
24
25      /omit-if-no-ref/
26      uart3m1_xfer: uart3m1-xfer {
27          rockchip,pins =
28              /* uart3_rxm1 */
29              <3 RK_PC0 4 &pcfg_pull_up>,
30              /* uart3_txm1 */
31              <3 RK_PB7 4 &pcfg_pull_up>;
32      };
33  };

第25~32行就是UART3所需的引脚配置。稍后我们向uart3中追加内容会使用到uart3m1_xfer这个节点的。
2、向uart3节点追加内容
在rk3568-atk-evb1-ddr4-v10.dtsi文件中追加相应如下代码:
示例代码32.4.2 串口的节点

1  &usart3 {    
2  		pinctrl-names = "default";
3   	pinctrl-0 = <&uart3m1_xfer>;
4   	status = "okay";
5  };
这里追加的内容很简单,第3行就是刚刚我们添加的pinmux配置。
3、设置串口的别名
在前面的UART驱动分析已经得知,驱动会读取aliaser节点,打开文件rk3568.dtsi,添加的别名如下所示:

示例代码32.4.3 串口的别名

1 aliases {
2   csi2dphy0 = &csi2_dphy0;
3   csi2dphy1 = &csi2_dphy1;
4   csi2dphy2 = &csi2_dphy2;
.....
24  serial0 = &uart0;
25  serial1 = &uart1;
26  serial2 = &uart2;
27  serial3 = &uart3;
28  serial4 = &uart4;
29  serial5 = &uart5;
30  serial6 = &uart6;
31  serial7 = &uart7;
32  serial8 = &uart8;
33  serial9 = &uart9;
39 };
serial3就是uart3的别名,表示在系统启动会生成名为“/dev/ttyS3”的设备文件,如此类推,最多是10个。serial2就是我们的调试串口。
启动开发板出厂buildroot系统,系统启动之后就会有如下32.4.4所示设备文件:

在这里插入图片描述

图32.4.4 串口的设备文件
使用系统自带的串口调试助手microcom来测试,在开发板上输入“microcom --version”来查看microcom工作是否正常,结果如图32.4.5所示
在这里插入图片描述

图32.4.5 microcom信息
32.5 RS232驱动测试
在测试之前要先将ATK-DLRK3568的RS232接口与电脑连接起来,具体接法可以参考开发板光盘A盘-基础资料\10、用户手册\01、测试文档\ ATK-DLRK3568_Buildroot系统快速体验手册/串口测试章节。
接好线之后在开发板中输入命令“microcom /dev/ttyS3 -s 115200”,打开串口功能,启动的是UART3,波特率为115200。这里COM3口是开发板的,COM6口是电脑端的。这里特别说明一下,microcom是没有开启回显功能的。
1、发送数据
在这里插入图片描述

图32.5.1 通过UART3向电脑发送“AAA”
在这里插入图片描述

图32.5.2 电脑接收开发板数据“AAA”
2、接收数据
在这里插入图片描述

图32.5.3 通过UART3向开发板发送“aaa”
在这里插入图片描述

图32.5.4 开发板接收电脑数据“aaa”
UART3收发测试都没有问题,说明我们的UART3驱动工作正常。如果要退出microcom功能的话,在开发板界面按下“CRTL+X”。关于microcom的使用我们这里讲的很简单,大家可以在网上查找更加详细的microcom使用教程。
32.6 RS485驱动编写
好了,我们接下来就来学习如何实现RS485,有了前面RS232的学习,RS485就比较轻松的。我们要做的就是在设备树中添加UART4对应的设备节点即可。
1、添加uart4的引脚信息
我们先在rk3568-pinctrl.dtsi文件下看有没有uart4的引脚配置,以及引脚配置是不是我开发板所对应的。默认情况下rk3568-pinctrl.dtsi里面是有uart4的引脚配置,如下图所示:
示例代码32.6.1 要追加的pinmux配置

1 uart4 {
2   /omit-if-no-ref/
3   uart4m0_xfer: uart4m0-xfer {
4       rockchip,pins =
5           /* uart4_rxm0 */
6           <1 RK_PA4 2 &pcfg_pull_up>,
7           /* uart4_txm0 */
8           <1 RK_PA6 2 &pcfg_pull_up>;
9   };
10...
25  /omit-if-no-ref/
26  uart4m1_xfer: uart4m1-xfer {
27      rockchip,pins =
28          /* uart4_rxm1 */
29          <3 RK_PB1 4 &pcfg_pull_up>,
30          /* uart4_txm1 */
31          <3 RK_PB2 4 &pcfg_pull_up>;
32  };
33  };

第25~32行就是UART4所需的引脚配置。稍后我们向uart4中追加内容会使用到uart3m1_xfer这个节点的。
2、向uart4节点追加内容
在rk3568-atk-evb1-ddr4-v10.dtsi文件中追加相应如下代码:
示例代码32.6.2 串口的节点

1  &usart4 {    
2  		pinctrl-names = "default";
3   	pinctrl-0 = <&uart4m1_xfer>;
4   	status = "okay";
5  };
这里追加的内容很简单,第3行就是刚刚我们添加的pinmux配置。

32.7 RS485驱动测试
在测试之前要先将ATK-DLRK3568的RS485接口与电脑连接起来,具体接法可以参考开发板光盘A盘-基础资料\10、用户手册\01、测试文档\ ATK-DLRK3568_Buildroot系统快速体验手册/串口测试章节。按32.5步骤测试RS485接口是否正常工作即可。

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值