add 09_UART/09,10,11
196
IMX6ULL/doc_pic/09_UART/05_a_在STM32MP157上做串口实验的准备工作.md
Normal 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
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 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,如下图:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
打开STM32MP157底板原理图,可知UART8_TX、UART8_RX使用引脚PE1、PE0,如下图:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.2 使能设备树节点
|
||||
|
||||
在STM32MP157的内核设备树文件`arch/arm/boot/dts/stm32mp151.dtsi`中,已经设置了uart8节点:
|
||||
|
||||

|
||||
|
||||
我们需要使能这个节点,往下看。
|
||||
|
||||
#### 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`中有了,无需我们添加:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.4 指定设备别名
|
||||
|
||||
UART8对应的设备节点是哪个?它的驱动程序需要从"别名"里确定编号。
|
||||
|
||||
修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-v1.dts`,如下:
|
||||
|
||||
```shell
|
||||
aliases {
|
||||
ethernet0 = ðernet0;
|
||||
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
|
||||
```
|
||||
|
||||
141
IMX6ULL/doc_pic/09_UART/09_UART驱动情景分析_open.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 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);
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/09_UART/09_UART驱动情景分析_open.tif
Normal file
172
IMX6ULL/doc_pic/09_UART/10_UART驱动情景分析_read.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 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`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 4.2 ldisk read
|
||||
|
||||
文件:`drivers\tty\n_tty.c`
|
||||
|
||||
函数:`n_tty_read`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
```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函数
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/09_UART/10_UART驱动情景分析_read.tif
Normal file
115
IMX6ULL/doc_pic/09_UART/11_UART驱动情景分析_write.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
### 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`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 3.2 ldisk write
|
||||
|
||||
文件:`drivers\tty\n_tty.c`
|
||||
|
||||
函数:`n_tty_write`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.3 uart_write
|
||||
|
||||
文件:`drivers\tty\serial\serial_core.c`
|
||||
|
||||
函数:`uart_write`
|
||||
|
||||

|
||||
|
||||
#### 3.3 硬件相关的发送
|
||||
|
||||
##### 3.3.1 IMX6ULL
|
||||
文件:`drivers\tty\serial\imx.c`
|
||||
|
||||
函数:`imx_start_tx`和`imx_txint`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
一开始时,发送buffer肯定为空,会立刻产生中断:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
##### 3.3.2 STM32MP157
|
||||
文件:`drivers\tty\serial\stm32-usart.c`
|
||||
|
||||
函数:`stm32_usart_threaded_interrupt`
|
||||
|
||||
STM32MP157发送串口数据时,有两种方法:DMA、中断。
|
||||
|
||||
我们来分析第二种方式:通过中断来发送数据。
|
||||
|
||||
* UART有Tx FIFO,可以往里面写入若干个数据,然后使能中断
|
||||
* 剩下的数据通过中断函数继续发送:
|
||||
|
||||

|
||||
BIN
IMX6ULL/doc_pic/09_UART/11_UART驱动情景分析_write.tif
Normal file
77
IMX6ULL/doc_pic/09_UART/12_UART驱动调试方法.md
Normal 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里:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 1.2 发送出去的数据
|
||||
|
||||
所有要发送出去的串口数据,都会通过uart_write函数发送,所有可以在uart_write中把它们打印出来:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. proc文件
|
||||
|
||||
|
||||
|
||||
#### 2.1 /proc/tty/drivers
|
||||
|
||||

|
||||
|
||||
#### 2.2 /proc/tty/driver(非常有用)
|
||||
|
||||

|
||||
|
||||
#### 2.3 /proc/tty/ldiscs
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 3. sys文件
|
||||
|
||||
在`drivers\tty\serial\serial_core.c`中,有如下代码:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
这写代码会在/sys目录中创建串口的对应文件,查看这些文件可以得到串口的很多参数。
|
||||
|
||||
怎么找到这些文件?在开发板上执行:
|
||||
|
||||
```shell
|
||||
cd /sys
|
||||
find -name uartclk // 就可以找到这些文件所在目录
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/09_UART/12_UART驱动调试方法.tif
Normal file
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/22_stm32mp157_uart8_node.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/25_tty_open.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/26_tty_read.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/27_ldisc_read.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/28_ldisc_write.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/29_ldisc_write.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/30_uart_write.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/31_imx6ull_start_tx.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/32_imx6ull_txint_isr.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/33_stm32mp157_start_tx.png
Normal file
|
After Width: | Height: | Size: 368 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/34_uart_sys.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/35_proc_tty_driver.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/35_proc_tty_drivers.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/36_proc_tty_ldiscs.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/37_tty_port_buf.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
IMX6ULL/doc_pic/09_UART/pic/09_UART/38_uart_write.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
10
README.md
@@ -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
|
||||
|
||||
141
STM32MP157/doc_pic/09_UART/09_UART驱动情景分析_open.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 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);
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
STM32MP157/doc_pic/09_UART/09_UART驱动情景分析_open.tif
Normal file
172
STM32MP157/doc_pic/09_UART/10_UART驱动情景分析_read.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 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`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 4.2 ldisk read
|
||||
|
||||
文件:`drivers\tty\n_tty.c`
|
||||
|
||||
函数:`n_tty_read`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
```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函数
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
STM32MP157/doc_pic/09_UART/10_UART驱动情景分析_read.tif
Normal file
115
STM32MP157/doc_pic/09_UART/11_UART驱动情景分析_write.md
Normal 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. 源码框架回顾
|
||||
|
||||

|
||||
|
||||
### 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`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 3.2 ldisk write
|
||||
|
||||
文件:`drivers\tty\n_tty.c`
|
||||
|
||||
函数:`n_tty_write`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 3.3 uart_write
|
||||
|
||||
文件:`drivers\tty\serial\serial_core.c`
|
||||
|
||||
函数:`uart_write`
|
||||
|
||||

|
||||
|
||||
#### 3.3 硬件相关的发送
|
||||
|
||||
##### 3.3.1 IMX6ULL
|
||||
文件:`drivers\tty\serial\imx.c`
|
||||
|
||||
函数:`imx_start_tx`和`imx_txint`
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
一开始时,发送buffer肯定为空,会立刻产生中断:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
##### 3.3.2 STM32MP157
|
||||
文件:`drivers\tty\serial\stm32-usart.c`
|
||||
|
||||
函数:`stm32_usart_threaded_interrupt`
|
||||
|
||||
STM32MP157发送串口数据时,有两种方法:DMA、中断。
|
||||
|
||||
我们来分析第二种方式:通过中断来发送数据。
|
||||
|
||||
* UART有Tx FIFO,可以往里面写入若干个数据,然后使能中断
|
||||
* 剩下的数据通过中断函数继续发送:
|
||||
|
||||

|
||||
BIN
STM32MP157/doc_pic/09_UART/11_UART驱动情景分析_write.tif
Normal file
77
STM32MP157/doc_pic/09_UART/12_UART驱动调试方法.md
Normal 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里:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
#### 1.2 发送出去的数据
|
||||
|
||||
所有要发送出去的串口数据,都会通过uart_write函数发送,所有可以在uart_write中把它们打印出来:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. proc文件
|
||||
|
||||
|
||||
|
||||
#### 2.1 /proc/tty/drivers
|
||||
|
||||

|
||||
|
||||
#### 2.2 /proc/tty/driver(非常有用)
|
||||
|
||||

|
||||
|
||||
#### 2.3 /proc/tty/ldiscs
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 3. sys文件
|
||||
|
||||
在`drivers\tty\serial\serial_core.c`中,有如下代码:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
这写代码会在/sys目录中创建串口的对应文件,查看这些文件可以得到串口的很多参数。
|
||||
|
||||
怎么找到这些文件?在开发板上执行:
|
||||
|
||||
```shell
|
||||
cd /sys
|
||||
find -name uartclk // 就可以找到这些文件所在目录
|
||||
```
|
||||
|
||||
|
||||
|
||||
BIN
STM32MP157/doc_pic/09_UART/12_UART驱动调试方法.tif
Normal file
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/25_tty_open.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/26_tty_read.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/27_ldisc_read.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/28_ldisc_write.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/29_ldisc_write.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/30_uart_write.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/31_imx6ull_start_tx.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/32_imx6ull_txint_isr.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 368 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/34_uart_sys.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/35_proc_tty_driver.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/35_proc_tty_drivers.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/36_proc_tty_ldiscs.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/37_tty_port_buf.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
STM32MP157/doc_pic/09_UART/pic/09_UART/38_uart_write.png
Normal file
|
After Width: | Height: | Size: 56 KiB |