add 09_UART/09,10,11

This commit is contained in:
weidongshan
2021-07-26 17:10:26 +08:00
parent b841ad8bfd
commit 0a47dcf560
51 changed files with 1215 additions and 1 deletions

View File

@@ -0,0 +1,196 @@
## 在STM32MP157上做串口实验的准备工作
* 本节课程源码在GIT仓库里
```shell
doc_and_source_for_drivers\STM32MP157\source\A7\09_UART
00_stm32mp157_devicetree_for_uart8
```
### 1. 出厂系统不支持扩展板上的UART
![](../../../IMX6ULL/doc_pic/09_UART/pic/09_UART/18_gps_to_stm32mp157.png)
### 2. 只关注APP开发的话
把GIT仓库中这个dtb文件复制到开发板上去就可以
```shell
doc_and_source_for_drivers\STM32MP157\source\A7\09_UART\00_stm32mp157_devicetree_for_uart8\dtb
stm32mp157c-100ask-512d-lcd-v1.dtb
```
操作方法:
* Ubuntu上复制dtb文件到NFS目录
```shell
$ cp stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
```
* 开发板上挂载NFS文件系统
* vmware使用NAT(假设windowsIP为193.168.1.100)
```shell
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
193.168.1.100:/home/book/nfs_rootfs /mnt
```
* vmware使用桥接或者不使用vmware而是直接使用服务器假设Ubuntu IP为193.168.1.137
```shell
[root@100ask:~]# mount -t nfs -o nolock,vers=3 193.168.1.137:/home/book/nfs_rootfs /mnt
```
* 更新设备树
```shell
[root@100ask:~]# mount /dev/mmcblk2p2 /boot
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot
[root@100ask:~]# sync
```
* 重启开发板
* 在串口可以看到以下设备节点
```shell
ls /dev/ttySTM*
/dev/ttySTM0
/dev/ttySTM1
/dev/ttySTM3
```
### 3. 从头修改设备树文件
#### 3.1 确定硬件资源
观察STM32MP157的扩展板背后写着: UART8_TX, UART8_RX如下图
![image-20210719092503508](pic/09_UART/19_stm32mp157_extend_board_back.png)
打开STM32MP157底板原理图可知UART8_TX、UART8_RX使用引脚PE1、PE0如下图
![image-20210719092726986](pic/09_UART/20_stm32mp157_pins_of_uart8.png)
#### 3.2 使能设备树节点
在STM32MP157的内核设备树文件`arch/arm/boot/dts/stm32mp151.dtsi`中已经设置了uart8节点
![image-20210719092138173](pic/09_UART/22_stm32mp157_uart8_node.png)
我们需要使能这个节点,往下看。
#### 3.3 通过Pinctrl指定引脚
光使能UART8还不行还需要为它选择引脚。
修改`arch/arm/boot/dts/stm32mp15xx-100ask.dtsi`,如下:
```shell
&uart8 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&uart8_pins_mx>;
pinctrl-1 = <&uart8_sleep_pins_mx>;
status = "okay";
};
```
这些Pinctrl信息已经在`arm/boot/dts/stm32mp157-100ask-pinctrl.dtsi`中有了,无需我们添加:
![image-20210719092949939](pic/09_UART/21_stm32mp157_uart8_pinctrl.png)
#### 3.4 指定设备别名
UART8对应的设备节点是哪个它的驱动程序需要从"别名"里确定编号。
修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-v1.dts`,如下:
```shell
aliases {
ethernet0 = &ethernet0;
serial0 = &uart4; //debug
serial1 = &usart6; //rs485
serial2 = &usart1; //bluetooth
serial3 = &uart8; // extend board uart, /dev/ttySTM3
};
```
#### 3.5 编译、使用
* 设置工具链
```shell
export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
```
* 编译设备树:
在Ubuntu的STM32MP157内核目录下执行如下命令,
得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb`
```shell
make dtbs
```
* 复制到NFS目录
```shell
$ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
```
* 开发板上挂载NFS文件系统
* vmware使用NAT(假设windowsIP为193.168.1.100)
```shell
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
193.168.1.100:/home/book/nfs_rootfs /mnt
```
* vmware使用桥接或者不使用vmware而是直接使用服务器假设Ubuntu IP为193.168.1.137
```shell
[root@100ask:~]# mount -t nfs -o nolock,vers=3 193.168.1.137:/home/book/nfs_rootfs /mnt
```
* 更新设备树
```shell
[root@100ask:~]# mount /dev/mmcblk2p2 /boot
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot
[root@100ask:~]# sync
```
* 重启开发板
* 在串口可以看到以下设备节点
```shell
ls /dev/ttySTM*
/dev/ttySTM0
/dev/ttySTM1
/dev/ttySTM3
```

View File

@@ -0,0 +1,141 @@
## UART驱动情景分析_open
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210722145518292](pic/09_UART/24_tty_driver_level_2.png)
### 3. 设备节点是哪个
* 为什么是/dev/ttymxc、/dev/ttySTM
* 为什么是/dev/ttymxc5、/dev/ttySTM3
### 4. open过程分析
它要做的事情:
* 找到tty_driver
* 分配/设置tty_struct
* 行规程相关的初始化
* 调用tty_driver->ops->open // ops是struct tty_operations指针类型, 就是uart_open
* tty_port_open // uart下有多个port, 下面就是打开某个port
* port->ops->activate(port, tty); // ops是struct tty_port_operations指针类型
* uart_startup(tty, state, 0);
* uart_port_startup(tty, state, init_hw);
* 调用uart_port->ops->startup // ops是struct uart_ops指针类型
图形化说明有3种ops
![image-20210723194028412](pic/09_UART/25_tty_open.png)
#### 4.1 tty_open
```c
tty_open
// 如果设备节点是(5,0)也就是/dev/tty, 表示当前TTY
// 对于普通串口, 第一次open时必定失败
tty = tty_open_current_tty(device, filp);
// 第一次open串口时走这个分支
if (!tty)
// 通过driver来open tty就是找到tty_driver然后分配/设置tty_struct
tty = tty_open_by_driver(device, inode, filp);
// 1. 先找到对应的tty_driver
driver = tty_lookup_driver(device, filp, &index);
// 2. 如果曾经open过会有对应的tty_struct
tty = tty_driver_lookup_tty(driver, filp, index);
// 3. 第1打开这个串口时肯定没有对应的tty_struct
// 所以使用下面的函数初始化设备
tty = tty_init_dev(driver, index);
// 3.1 分配tty_strct
tty = alloc_tty_struct(driver, idx);
tty->ops = driver->ops;
// 3.2 安装tty: 也就是driver->ttys[tty->index] = tty;
retval = tty_driver_install_tty(driver, tty);
// 3.3 调用行规程的open函数, 过于复杂,不分析
// n_tty.c中的n_tty_open函数
retval = tty_ldisc_setup(tty, tty->link);
......
// ops是tty_operations类型
// 对于串口ops就是serial_core.c中的uart_ops
// uart_open
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
else
retval = -ENODEV;
```
#### 4.2 uart_open
```c
uart_open
tty_port_open
// ops是tty_port_operations类型对应serial_core.c中的uart_port_ops
// uart_port_activate
if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
return retval;
}
}
uart_port_activate
uart_startup
uart_port_startup
// ops是uart_ops类型在硬件驱动中设置
// 硬件相关的驱动中uart_port的uart_ops里必须提供startup函数
retval = uport->ops->startup(uport);
```

View File

@@ -0,0 +1,172 @@
## UART驱动情景分析_read
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210722145518292](pic/09_UART/24_tty_driver_level_2.png)
### 3. 使用哪个行规程
#### 3.1 行规程注册
文件:`drivers\tty\n_tty.c`
```c
void __init n_tty_init(void)
{
tty_register_ldisc(N_TTY, &n_tty_ops);
}
```
以后可以通过标号N_TTY找到这个行规程。
#### 3.2 open设备时确定行规程
```c
tty_open
tty_open_by_driver
tty_init_dev
tty = alloc_tty_struct(driver, idx);
tty_ldisc_init(tty);
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
tty->ldisc = ld;
```
### 4. read过程分析
流程为:
* APP读
* 使用行规程来读
* 无数据则休眠
* UART接收到数据产生中断
* 中断程序从硬件上读入数据
* 发给行规程
* 行规程处理后存入buffer
* 行规程唤醒APP
* APP被唤醒后从行规程buffer中读入数据返回
#### 4.1 tty_read
文件:`drivers\tty\tty_io.c`
![](pic/09_UART/26_tty_read.png)
#### 4.2 ldisk read
文件:`drivers\tty\n_tty.c`
函数:`n_tty_read`
![image-20210724095007517](pic/09_UART/27_ldisc_read.png)
```c
copy_from_read_buf
const unsigned char *from = read_buf_addr(ldata, tail);
// return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
retval = copy_to_user(*b, from, n);
```
#### 4.3 数据源头: 中断
##### 4.3.1 IMX6ULL
文件:`drivers\tty\serial\imx.c`
函数:`imx_rxint`
```c
imx_rxint
// 读取硬件状态
// 得到数据
// 在对应的uart_port中更新统计信息, 比如sport->port.icount.rx++;
// 把数据存入tty_port里的tty_buffer
tty_insert_flip_char(port, rx, flg)
// 通知行规程来处理
tty_flip_buffer_push(port);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数
```
##### 4.3.2 STM32MP157
文件:`drivers\tty\serial\stm32-usart.c`
函数:`stm32_usart_threaded_interrupt`
```c
stm32_usart_threaded_interrupt
stm32_usart_receive_chars(port, true);
// 通过DMA方式得到数据
stm32_usart_receive_chars_dma(port);
stm32_usart_push_buffer_dma(port, dma_size);
// 把数据存入tty_port里的tty_buffer
dma_count = tty_insert_flip_string(ttyport, dma_start, dma_size);
// 更新统计信息
port->icount.rx += dma_count;
// 通知行规程来处理
tty_flip_buffer_push(tport);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数
```

View File

@@ -0,0 +1,115 @@
## UART驱动情景分析_write
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210724153515059](pic/09_UART/24_tty_driver_level_2.png)
### 3. write过程分析
流程为:
* APP写
* 使用行规程来写
* 数据最终存入uart_state->xmit的buffer里
* 硬件发送:怎么发送数据?
* 使用硬件驱动中uart_ops->start_tx开始发送
* 具体的发送方法有2种通过DMA或通过中断
* 中断方式
* 方法1直接使能 tx empty中断一开始tx buffer为空在中断里填入数据
* 方法2写部分数据到tx fifo使能中断剩下的数据再中断里继续发送
#### 3.1 tty_write
文件:`drivers\tty\tty_io.c`
![image-20210724114102036](pic/09_UART/28_ldisc_write.png)
#### 3.2 ldisk write
文件:`drivers\tty\n_tty.c`
函数:`n_tty_write`
![image-20210724115304725](pic/09_UART/29_ldisc_write.png)
#### 3.3 uart_write
文件:`drivers\tty\serial\serial_core.c`
函数:`uart_write`
![image-20210724120514017](pic/09_UART/30_uart_write.png)
#### 3.3 硬件相关的发送
##### 3.3.1 IMX6ULL
文件:`drivers\tty\serial\imx.c`
函数:`imx_start_tx`和`imx_txint`
![image-20210724121404872](pic/09_UART/31_imx6ull_start_tx.png)
一开始时发送buffer肯定为空会立刻产生中断
![image-20210724121823272](pic/09_UART/32_imx6ull_txint_isr.png)
##### 3.3.2 STM32MP157
文件:`drivers\tty\serial\stm32-usart.c`
函数:`stm32_usart_threaded_interrupt`
STM32MP157发送串口数据时有两种方法DMA、中断。
我们来分析第二种方式:通过中断来发送数据。
* UART有Tx FIFO可以往里面写入若干个数据然后使能中断
* 剩下的数据通过中断函数继续发送:
![image-20210724142304181](pic/09_UART/33_stm32mp157_start_tx.png)

View File

@@ -0,0 +1,77 @@
## UART驱动调试方法
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 怎么得到UART硬件上收发的数据
#### 1.1 接收到的原始数据
可以在接收中断函数里把它打印出来这些数据也会存入UART对应的tty_port的buffer里
![image-20210724170325505](pic/09_UART/37_tty_port_buf.png)
#### 1.2 发送出去的数据
所有要发送出去的串口数据都会通过uart_write函数发送所有可以在uart_write中把它们打印出来
![image-20210724171004649](pic/09_UART/38_uart_write.png)
### 2. proc文件
#### 2.1 /proc/tty/drivers
![image-20210724165611075](pic/09_UART/35_proc_tty_drivers.png)
#### 2.2 /proc/tty/driver(非常有用)
![image-20210724165934593](pic/09_UART/35_proc_tty_driver.png)
#### 2.3 /proc/tty/ldiscs
![image-20210724170038480](pic/09_UART/36_proc_tty_ldiscs.png)
### 3. sys文件
在`drivers\tty\serial\serial_core.c`中,有如下代码:
![image-20210724164455033](pic/09_UART/34_uart_sys.png)
这写代码会在/sys目录中创建串口的对应文件查看这些文件可以得到串口的很多参数。
怎么找到这些文件?在开发板上执行:
```shell
cd /sys
find -name uartclk // 就可以找到这些文件所在目录
```

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -345,7 +345,15 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
```shell
08_UART驱动情景分析_注册
```
* 2021.07.26 发布"UART子系统"
```shell
09_UART驱动情景分析_open
10_UART驱动情景分析_read
11_UART驱动情景分析_write
```
## 6. 联系方式
* 官网http://www.100ask.net

View File

@@ -0,0 +1,141 @@
## UART驱动情景分析_open
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210722145518292](pic/09_UART/24_tty_driver_level_2.png)
### 3. 设备节点是哪个
* 为什么是/dev/ttymxc、/dev/ttySTM
* 为什么是/dev/ttymxc5、/dev/ttySTM3
### 4. open过程分析
它要做的事情:
* 找到tty_driver
* 分配/设置tty_struct
* 行规程相关的初始化
* 调用tty_driver->ops->open // ops是struct tty_operations指针类型, 就是uart_open
* tty_port_open // uart下有多个port, 下面就是打开某个port
* port->ops->activate(port, tty); // ops是struct tty_port_operations指针类型
* uart_startup(tty, state, 0);
* uart_port_startup(tty, state, init_hw);
* 调用uart_port->ops->startup // ops是struct uart_ops指针类型
图形化说明有3种ops
![image-20210723194028412](pic/09_UART/25_tty_open.png)
#### 4.1 tty_open
```c
tty_open
// 如果设备节点是(5,0)也就是/dev/tty, 表示当前TTY
// 对于普通串口, 第一次open时必定失败
tty = tty_open_current_tty(device, filp);
// 第一次open串口时走这个分支
if (!tty)
// 通过driver来open tty就是找到tty_driver然后分配/设置tty_struct
tty = tty_open_by_driver(device, inode, filp);
// 1. 先找到对应的tty_driver
driver = tty_lookup_driver(device, filp, &index);
// 2. 如果曾经open过会有对应的tty_struct
tty = tty_driver_lookup_tty(driver, filp, index);
// 3. 第1打开这个串口时肯定没有对应的tty_struct
// 所以使用下面的函数初始化设备
tty = tty_init_dev(driver, index);
// 3.1 分配tty_strct
tty = alloc_tty_struct(driver, idx);
tty->ops = driver->ops;
// 3.2 安装tty: 也就是driver->ttys[tty->index] = tty;
retval = tty_driver_install_tty(driver, tty);
// 3.3 调用行规程的open函数, 过于复杂,不分析
// n_tty.c中的n_tty_open函数
retval = tty_ldisc_setup(tty, tty->link);
......
// ops是tty_operations类型
// 对于串口ops就是serial_core.c中的uart_ops
// uart_open
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
else
retval = -ENODEV;
```
#### 4.2 uart_open
```c
uart_open
tty_port_open
// ops是tty_port_operations类型对应serial_core.c中的uart_port_ops
// uart_port_activate
if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
return retval;
}
}
uart_port_activate
uart_startup
uart_port_startup
// ops是uart_ops类型在硬件驱动中设置
// 硬件相关的驱动中uart_port的uart_ops里必须提供startup函数
retval = uport->ops->startup(uport);
```

View File

@@ -0,0 +1,172 @@
## UART驱动情景分析_read
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210722145518292](pic/09_UART/24_tty_driver_level_2.png)
### 3. 使用哪个行规程
#### 3.1 行规程注册
文件:`drivers\tty\n_tty.c`
```c
void __init n_tty_init(void)
{
tty_register_ldisc(N_TTY, &n_tty_ops);
}
```
以后可以通过标号N_TTY找到这个行规程。
#### 3.2 open设备时确定行规程
```c
tty_open
tty_open_by_driver
tty_init_dev
tty = alloc_tty_struct(driver, idx);
tty_ldisc_init(tty);
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
tty->ldisc = ld;
```
### 4. read过程分析
流程为:
* APP读
* 使用行规程来读
* 无数据则休眠
* UART接收到数据产生中断
* 中断程序从硬件上读入数据
* 发给行规程
* 行规程处理后存入buffer
* 行规程唤醒APP
* APP被唤醒后从行规程buffer中读入数据返回
#### 4.1 tty_read
文件:`drivers\tty\tty_io.c`
![](pic/09_UART/26_tty_read.png)
#### 4.2 ldisk read
文件:`drivers\tty\n_tty.c`
函数:`n_tty_read`
![image-20210724095007517](pic/09_UART/27_ldisc_read.png)
```c
copy_from_read_buf
const unsigned char *from = read_buf_addr(ldata, tail);
// return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
retval = copy_to_user(*b, from, n);
```
#### 4.3 数据源头: 中断
##### 4.3.1 IMX6ULL
文件:`drivers\tty\serial\imx.c`
函数:`imx_rxint`
```c
imx_rxint
// 读取硬件状态
// 得到数据
// 在对应的uart_port中更新统计信息, 比如sport->port.icount.rx++;
// 把数据存入tty_port里的tty_buffer
tty_insert_flip_char(port, rx, flg)
// 通知行规程来处理
tty_flip_buffer_push(port);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数
```
##### 4.3.2 STM32MP157
文件:`drivers\tty\serial\stm32-usart.c`
函数:`stm32_usart_threaded_interrupt`
```c
stm32_usart_threaded_interrupt
stm32_usart_receive_chars(port, true);
// 通过DMA方式得到数据
stm32_usart_receive_chars_dma(port);
stm32_usart_push_buffer_dma(port, dma_size);
// 把数据存入tty_port里的tty_buffer
dma_count = tty_insert_flip_string(ttyport, dma_start, dma_size);
// 更新统计信息
port->icount.rx += dma_count;
// 通知行规程来处理
tty_flip_buffer_push(tport);
tty_schedule_flip(port);
queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理
// 对应flush_to_ldisc函数
```

View File

@@ -0,0 +1,115 @@
## UART驱动情景分析_write
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 情景分析大纲
* 注册过程分析
* open过程分析
* read过程分析
* write过程分析
### 2. 源码框架回顾
![image-20210724153515059](pic/09_UART/24_tty_driver_level_2.png)
### 3. write过程分析
流程为:
* APP写
* 使用行规程来写
* 数据最终存入uart_state->xmit的buffer里
* 硬件发送:怎么发送数据?
* 使用硬件驱动中uart_ops->start_tx开始发送
* 具体的发送方法有2种通过DMA或通过中断
* 中断方式
* 方法1直接使能 tx empty中断一开始tx buffer为空在中断里填入数据
* 方法2写部分数据到tx fifo使能中断剩下的数据再中断里继续发送
#### 3.1 tty_write
文件:`drivers\tty\tty_io.c`
![image-20210724114102036](pic/09_UART/28_ldisc_write.png)
#### 3.2 ldisk write
文件:`drivers\tty\n_tty.c`
函数:`n_tty_write`
![image-20210724115304725](pic/09_UART/29_ldisc_write.png)
#### 3.3 uart_write
文件:`drivers\tty\serial\serial_core.c`
函数:`uart_write`
![image-20210724120514017](pic/09_UART/30_uart_write.png)
#### 3.3 硬件相关的发送
##### 3.3.1 IMX6ULL
文件:`drivers\tty\serial\imx.c`
函数:`imx_start_tx`和`imx_txint`
![image-20210724121404872](pic/09_UART/31_imx6ull_start_tx.png)
一开始时发送buffer肯定为空会立刻产生中断
![image-20210724121823272](pic/09_UART/32_imx6ull_txint_isr.png)
##### 3.3.2 STM32MP157
文件:`drivers\tty\serial\stm32-usart.c`
函数:`stm32_usart_threaded_interrupt`
STM32MP157发送串口数据时有两种方法DMA、中断。
我们来分析第二种方式:通过中断来发送数据。
* UART有Tx FIFO可以往里面写入若干个数据然后使能中断
* 剩下的数据通过中断函数继续发送:
![image-20210724142304181](pic/09_UART/33_stm32mp157_start_tx.png)

View File

@@ -0,0 +1,77 @@
## UART驱动调试方法
参考资料
* 参考代码:
```shell
硬件相关:
drivers/tty/serial/imx.c
drivers/tty/serial/stm32-usart.c
串口核心层:
drivers/tty/serial/serial_core.c
TTY层:
drivers/tty/tty_io.c
```
### 1. 怎么得到UART硬件上收发的数据
#### 1.1 接收到的原始数据
可以在接收中断函数里把它打印出来这些数据也会存入UART对应的tty_port的buffer里
![image-20210724170325505](pic/09_UART/37_tty_port_buf.png)
#### 1.2 发送出去的数据
所有要发送出去的串口数据都会通过uart_write函数发送所有可以在uart_write中把它们打印出来
![image-20210724171004649](pic/09_UART/38_uart_write.png)
### 2. proc文件
#### 2.1 /proc/tty/drivers
![image-20210724165611075](pic/09_UART/35_proc_tty_drivers.png)
#### 2.2 /proc/tty/driver(非常有用)
![image-20210724165934593](pic/09_UART/35_proc_tty_driver.png)
#### 2.3 /proc/tty/ldiscs
![image-20210724170038480](pic/09_UART/36_proc_tty_ldiscs.png)
### 3. sys文件
在`drivers\tty\serial\serial_core.c`中,有如下代码:
![image-20210724164455033](pic/09_UART/34_uart_sys.png)
这写代码会在/sys目录中创建串口的对应文件查看这些文件可以得到串口的很多参数。
怎么找到这些文件?在开发板上执行:
```shell
cd /sys
find -name uartclk // 就可以找到这些文件所在目录
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB