实验 10-1 UART驱动分析
- 格式:doc
- 大小:167.00 KB
- 文档页数:17
uart模块的驱动方法
UART(Universal Asynchronous Receiver/Transmitter)是一种常见的串行通信接口,用于在嵌入式系统中实现设备之间的通信。
在嵌入式系统中,UART模块的驱动方法通常涉及以下几个方面:
1. 初始化,首先,需要配置UART模块的参数,包括波特率、
数据位、停止位和校验位等。
这些参数的设置需要根据具体的应用
需求来确定。
通常情况下,需要根据目标设备的规格书来配置这些
参数。
2. 发送数据,一旦UART模块初始化完成,就可以使用相应的
函数来发送数据。
通常情况下,需要将待发送的数据写入到UART发
送缓冲区,然后UART模块会自动将数据发送出去。
3. 接收数据,接收数据的方法通常涉及设置接收中断或者轮询
方式。
在接收中断方式下,当接收到数据时,会触发中断,然后可
以在中断服务程序中处理接收到的数据。
而在轮询方式下,程序会
定期查询接收缓冲区是否有新的数据到达。
4. 错误处理,UART模块在传输过程中可能会出现一些错误,
比如校验错误、帧错误等。
驱动程序需要能够检测并处理这些错误,以确保数据的可靠传输。
5. 控制流,有时候需要在UART通信中实现流控制,比如硬件
流控或软件流控。
驱动程序需要支持这些流控制方式,并能够根据
需要进行配置。
总的来说,UART模块的驱动方法需要考虑到初始化、数据发送、数据接收、错误处理和流控制等多个方面。
针对不同的应用场景和
目标设备,驱动方法可能会有所不同,需要根据具体情况进行调整
和优化。
uart实验报告
《UART实验报告》
实验目的:通过实验学习串行通信的基本原理,掌握UART通信协议的工作原理和使用方法。
实验设备:单片机开发板、串口调试助手、电脑。
实验原理:UART(Universal Asynchronous Receiver/Transmitter)是一种通用的异步串行通信协议,用于在计算机和外部设备之间进行数据传输。
UART通信协议包括数据位、停止位、奇偶校验位等参数,通过这些参数的设置可以实现不同的通信速率和数据传输方式。
实验步骤:
1. 连接单片机开发板和电脑,打开串口调试助手。
2. 在单片机开发板上编写UART通信程序,设置通信参数。
3. 将单片机开发板通过串口连接到电脑,打开串口调试助手。
4. 在串口调试助手上发送数据,观察单片机开发板接收到的数据。
5. 在单片机开发板上发送数据,观察串口调试助手接收到的数据。
实验结果:
经过实验,我们成功地实现了通过UART通信协议在单片机开发板和电脑之间进行数据传输。
在串口调试助手上发送的数据能够被单片机开发板正确接收,并且在单片机开发板上发送的数据也能够被串口调试助手正确接收。
通过调整通信参数,我们还验证了不同通信速率和数据传输方式对通信效果的影响。
实验总结:
通过本次实验,我们深入了解了UART通信协议的工作原理和使用方法,掌握
了串行通信的基本原理。
在今后的学习和工作中,我们将能够更加熟练地应用UART通信协议进行数据传输,为实际工程应用打下了坚实的基础。
嵌⼊式系统实验—通⽤异步收发器(UART)实验实验五:通⽤异步收发器(UART)实验Technorati 标签: 嵌⼊式系统实验,通⽤异步收发器,UART,mini2440,arm,j-link,keil-uvision,实验报告⼀、实验⽬的1、掌握 UART 外设的操作原理和编程。
2、学习使⽤ UART 进⾏多机通讯。
⼆、实验设备1、硬件:PC 机⼀台、Mini2440 ARM 实验板⼀套 J-link 仿真器⼀套2、软件:WindowsXP 系统,Keil uVision 4.0 集成开发环境三、实验内容(1)使⽤ C 语⾔编写 UART 基本收发数据程序,进⾏ 2 个实验板之间的数据收发测试。
(2)⽤两个实验板模拟嵌⼊式控制系统中的数据采集/控制实验,其中⼀个实验板模拟数据采集模块,将通过UART 返回数据;另⼀块实验板模拟控制系统的主机,通过 UART 采集数据,并通过 UART 发出控制指令。
四、实验预习要求(1)学习 UART 相关的原理概念;(2)查阅 S3C2440 芯⽚⼿册,了解 UART0 结构和原理。
五、实验步骤(1)启动 Keil uVision,新建⼀个⼯程ex05。
不需要系统提供的 Startup ⽂件。
建⽴汇编源⽂件 ex05.s,编写实验程序,然后添加到⼯程中。
设置⼯程选项,存储器映射。
设置⼯程调试选项。
建⽴仿真初始化⽂件 RAM.ini。
(2)建⽴ C 语⾔源⽂件 main.c,编写实验程序,然后添加到⼯程中。
(3)使⽤交叉串⼝电缆连接两个实验板。
(4)编译程序,使⽤仿真器在⽬标板上调试运⾏程序,使⽤单步、设置断点,观察程序执⾏时,收发数据的值。
六、实验程序C 语⾔实验程序见程序清单 5。
程序清单 4.1 UART 实验程序// Uart0#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)#define RdURXH0() (*(volatile unsigned char *)0x50000024)#define rULCON0 (*(volatile unsigned *)0x50000000) //UART 0 Line control#define rUCON0 (*(volatile unsigned *)0x50000004) //UART 0 Control#define rUFCON0 (*(volatile unsigned *)0x50000008) //UART 0 FIFO control#define rUMCON0 (*(volatile unsigned *)0x5000000c) //UART 0 Modem control#define rUTRSTAT0 (*(volatile unsigned *)0x50000010) //UART 0 Tx/Rx status#define rUERSTAT0 (*(volatile unsigned *)0x50000014) //UART 0 Rx error status#define rUFSTAT0 (*(volatile unsigned *)0x50000018) //UART 0 FIFO status#define rUMSTAT0 (*(volatile unsigned *)0x5000001c) //UART 0 Modem status#define rUBRDIV0 (*(volatile unsigned *)0x50000028) //UART 0 Baud rate divisor#define rGPHCON (*(volatile unsigned *)0x56000070) //Port H control#define rGPHUP (*(volatile unsigned *)0x56000078) //Pull-up control H//PCLK:12MHz#define PCLK 12000000//( (int)(pclk/16./baud+0.5) -1 )#define baud_value 12 //57600void Uart_Init(){int i;rUFCON0 = 0x0; //UART channel 0 FIFO control register, FIFO disablerUMCON0 = 0x0; //UART chaneel 0 MODEM control register, AFC disablerULCON0 = 0x3; //Line control register : Normal,No parity,1 stop,8 bits// [10] [9] [8] [7] [6] [5] [4] [3:2] [1:0]// Clock Sel, Tx Int, Rx Int, Rx Time Out, Rx err, Loop-back, Send break, TransmitMode, Receive Mode// 0 1 0 , 0 1 0 0 ,01 01// PCLK Level Pulse Disable Generate Normal NormalInterrupt or PollingrUCON0 = 0x245; // Control registerrUBRDIV0= baud_value; //Baud rate divisior register 0for(i=0;i<100;i++);rGPHCON |= 0xaa;//use GPH port as uart0rGPHUP =0x0f;//the pull up function is disabled}void Uart_SendByte(int data){WrUTXH0(data);char Uart_Getch(void){while(!(rUTRSTAT0 & 0x1)) //Receive data readyreturn RdURXH0();}main(){char c = 'a';Uart_Init();Uart_SendByte(c);while(1){c = Uart_Getch();c++;Uart_SendByte(c);}七、实验现象两块实验板在接收指令处设断点,然后实验板1全速运⾏,实验板2接着全速运⾏,实验板1⾃动暂停。
第1篇一、实验背景与目的随着计算机技术的飞速发展,操作系统对硬件设备的支持越来越丰富。
设备驱动程序作为操作系统与硬件之间的桥梁,扮演着至关重要的角色。
本实验旨在通过学习Linux字符设备驱动的开发,加深对设备驱动程序的理解,提高实践能力。
二、实验环境与工具1. 操作系统:Linux Ubuntu 20.042. 编程语言:C3. 开发工具:gcc、make4. 驱动框架:Linux内核三、实验内容本实验主要完成以下内容:1. 字符设备驱动程序的基本框架2. 字符设备的打开、读取、写入和关闭操作3. 字符设备驱动的注册与注销4. 字符设备驱动的用户空间交互四、实验步骤1. 创建设备文件首先,我们需要在`/dev`目录下创建一个名为`mychar`的字符设备文件。
可以使用以下命令:```bashmknod /dev/mychar c 123 0```其中,`123`是主设备号,`0`是次设备号。
2. 编写字符设备驱动程序创建一个名为`mychar.c`的文件,并编写以下代码:```cinclude <linux/module.h>include <linux/fs.h>include <linux/uaccess.h>static int major = 123; // 设备号static int device_open(struct inode inode, struct file filp);static int device_release(struct inode inode, struct file filp);static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos);static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos);static struct file_operations fops = {.open = device_open,.release = device_release,.read = device_read,.write = device_write,};static int __init mychar_init(void) {major = register_chrdev(0, "mychar", &fops);if (major < 0) {printk(KERN_ALERT "mychar: can't get major number\n");return major;}printk(KERN_INFO "mychar: registered correctly with major number %d\n", major);return 0;}static void __exit mychar_exit(void) {unregister_chrdev(major, "mychar");printk(KERN_INFO "mychar: Goodbye from the LKM!\n");}static int device_open(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been opened\n");return 0;}static int device_release(struct inode inode, struct file filp) {printk(KERN_INFO "mychar: Device has been closed\n");return 0;}static ssize_t device_read(struct file filp, char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been read\n");return count;}static ssize_t device_write(struct file filp, const char __user buf, size_t count, loff_t pos) {printk(KERN_INFO "mychar: Device has been written\n"); return count;}module_init(mychar_init);module_exit(mychar_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A simple character device driver");```保存文件,并使用以下命令编译:```bashmake```3. 加载字符设备驱动程序将编译生成的`mychar.ko`文件加载到内核中:```bashinsmod mychar.ko```4. 测试字符设备驱动程序使用以下命令查看`/dev/mychar`设备文件:```bashls -l /dev/mychar```使用`cat`命令测试读取和写入操作:```bashcat /dev/mycharecho "Hello, world!" > /dev/mychar```观察系统日志,确认驱动程序的打开、读取、写入和关闭操作。
uart驱动调试方法UART驱动是一种用于串口通信的驱动程序,常用于嵌入式系统中。
调试UART驱动的目的是确保其正常工作,并排除可能的问题。
下面是一些调试UART驱动的方法:1.检查硬件连接:确保UART的引脚正确连接到目标设备上,并且没有虚焊、短路或其他硬件问题。
需要检查TX、RX、地线和电源线的正确连接。
2.配置正确的波特率:确认驱动程序和目标设备的波特率设置一致。
如果波特率设置不正确,通信将无法成功。
3.检查中断和DMA:如果使用中断或DMA进行数据传输,在调试过程中需要确保它们的配置和使用正确。
确认中断和DMA的初始化和处理函数是否正确,以及是否可靠。
4.使用调试工具:使用调试工具可以帮助检测和解决UART驱动的问题。
例如,使用示波器可以观察波形是否符合预期,使用串口调试助手可以查看发送和接收的数据。
还可以使用软件调试器来观察代码执行的过程。
5.打印调试信息:在驱动程序中添加打印语句,以便在运行时输出调试信息。
可以打印各种变量、标志位和状态信息,以便跟踪代码的执行流程。
这种方法可以帮助定位并解决问题。
6.内部测试模式:一些UART控制器提供了内部测试模式,可以自动生成和接收特定模式的数据。
通过使用内部测试模式,可以排除硬件和物理连接的问题,并检查驱动程序的正确性。
7.理解数据协议:UART驱动中很重要的一点是理解通信的协议。
要确保驱动程序正确地构造和解析数据帧,包括起始位、停止位、校验位和数据位。
8.分阶段调试:UART驱动的调试可以分成多个阶段进行。
首先,确保驱动程序可以正常初始化和配置。
然后,测试发送和接收数据的功能。
最后,检查错误处理和异常情况的处理。
9.必要时查看硬件文档:如果遇到了很棘手的问题,无法通过常规的调试方法解决,可以查看硬件文档或厂商提供的技术支持。
硬件文档可以提供关于UART控制器的详细说明和配置建议。
10.与其他设备协同调试:UART驱动通常会与其他设备进行通信,例如处理器、外设或其他串口设备。
uart驱动电路设计摘要:1.UART 概述2.UART 驱动电路设计原则3.UART 驱动电路的主要组成部分4.UART 驱动电路设计流程5.设计实例与注意事项正文:一、UART 概述UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种广泛应用于电子设备中的串行通信接口。
它的主要功能是在发送端将数据字符从并行转换为串行,按位发送到接收端,在接收端将串行数据字符转换为并行数据,以便于设备处理。
UART在电子设备中具有重要作用,如计算机外设、通信设备等。
二、UART 驱动电路设计原则1.稳定性:驱动电路应具有良好的稳定性,确保数据传输的可靠性。
2.兼容性:驱动电路应能兼容不同厂商、不同型号的UART 设备。
3.低功耗:驱动电路应在满足性能要求的前提下,尽量降低功耗。
4.简洁性:驱动电路设计应尽量简洁,便于调试和维护。
三、UART 驱动电路的主要组成部分1.电源模块:为驱动电路提供稳定的电源。
2.晶振模块:提供驱动电路的工作时钟。
3.复位模块:为驱动电路提供复位信号。
4.电平转换模块:实现UART 接口的电平转换,如TTL 电平转换为CMOS 电平。
5.串行发送模块:将数据字符从并行转换为串行,按位发送。
6.串行接收模块:将串行数据字符转换为并行数据。
7.缓存模块:缓存发送和接收的数据,以适应不同速率的UART 设备。
四、UART 驱动电路设计流程1.需求分析:明确驱动电路的功能、性能、兼容性等要求。
2.电路设计:根据需求分析,设计驱动电路的各个模块,并选择合适的元器件。
3.电路仿真:使用仿真软件对驱动电路进行仿真测试,验证电路性能。
4.硬件调试:制作驱动电路硬件原型,进行实际硬件调试。
5.软件调试:编写驱动程序,对驱动电路进行功能测试。
6.性能测试:对驱动电路的稳定性、兼容性、功耗等性能进行测试。
7.优化与完善:根据测试结果,对驱动电路进行优化与完善。
一个好用的串口UART程序==========================================================================//-----------------------------------------------------// Design Name : uart// File Name : uart.v// Function : Simple UART// Coder : Deepak Kumar Tala//-----------------------------------------------------module uart (reset ,txclk ,ld_tx_data ,//tx_data ,tx_enable ,//tx_out ,tx_empty ,rxclk ,uld_rx_data ,rx_data ,rx_enable ,rx_in ,rx_empty);// Port declarationsinput reset ;input txclk ;input ld_tx_data ;input [7:0] tx_data ;input tx_enable ;output tx_out ;output tx_empty ;input rxclk ;input uld_rx_data ;output [7:0] rx_data ;input rx_enable ;input rx_in ;output rx_empty ;// Internal Variablesreg [7:0] tx_reg ;reg tx_empty ;reg tx_over_run ;reg [3:0] tx_cnt ;reg tx_out ;reg [7:0] rx_reg ;reg [7:0] rx_data ;reg [3:0] rx_sample_cnt ;reg [3:0] rx_cnt ;reg rx_frame_err ;reg rx_over_run ;reg rx_empty ;reg rx_d1 ;reg rx_d2 ;reg rx_busy ;// UART RX Logicalways @ (posedge rxclk or posedge reset) if (reset) beginrx_reg <= 0;rx_data <= 0;rx_sample_cnt <= 0;rx_cnt <= 0;rx_frame_err <= 0;rx_over_run <= 0;rx_empty <= 1;rx_d1 <= 1;rx_d2 <= 1;rx_busy <= 0;end else begin// Synchronize the asynch signalrx_d1 <= rx_in;rx_d2 <= rx_d1;// Uload the rx dataif (uld_rx_data) beginrx_data <= rx_reg;rx_empty <= 1;end// Receive data only when rx is enabledif (rx_enable) begin// Check if just received start of frameif (!rx_busy && !rx_d2) beginrx_busy <= 1;rx_sample_cnt <= 1;rx_cnt <= 0;end// Start of frame detected, Proceed with rest of data if (rx_busy) beginrx_sample_cnt <= rx_sample_cnt + 1;// Logic to sample at middle of dataif (rx_sample_cnt == 7) beginif ((rx_d2 == 1) && (rx_cnt == 0)) beginrx_busy <= 0;end else beginrx_cnt <= rx_cnt + 1;// Start storing the rx dataif (rx_cnt > 0 && rx_cnt < 9) beginrx_reg[rx_cnt - 1] <= rx_d2;endif (rx_cnt == 9) beginrx_busy <= 0;// Check if End of frame received correctly if (rx_d2 == 0) beginrx_frame_err <= 1;end else beginrx_empty <= 0;rx_frame_err <= 0;// Check if last rx data was not unloaded, rx_over_run <= (rx_empty) ? 0 : 1;endendendendendendif (!rx_enable) beginrx_busy <= 0;endend// UART TX Logicalways @ (posedge txclk or posedge reset) if (reset) begintx_reg <= 0;tx_empty <= 1;tx_over_run <= 0;tx_out <= 1;tx_cnt <= 0;end else beginif (ld_tx_data) beginif (!tx_empty) begintx_over_run <= 0;end else begintx_reg <= tx_data;tx_empty <= 0;endendif (tx_enable && !tx_empty) begintx_cnt <= tx_cnt + 1;if (tx_cnt == 0) begintx_out <= 0;endif (tx_cnt > 0 && tx_cnt < 9) begin tx_out <= tx_reg[tx_cnt -1];endif (tx_cnt == 9) begintx_out <= 1;tx_cnt <= 0;tx_empty <= 1;endendif (!tx_enable) begintx_cnt <= 0;endendEndmodule一个好用的串口UART程序转帖学习资料2009-08-05 09:33:10 阅读262 评论0 字号:大中小========================================================================== //-----------------------------------------------------// Design Name : uart// File Name : uart.v// Function : Simple UART// Coder : Deepak Kumar Tala//-----------------------------------------------------module uart (reset ,txclk ,ld_tx_data ,tx_data ,tx_enable ,tx_out ,tx_empty ,rxclk ,uld_rx_data ,rx_data ,rx_enable ,rx_in ,rx_empty);// Port declarationsinput reset ; input txclk ; input ld_tx_data ; input [7:0] tx_data ; input tx_enable ; output tx_out ; output tx_empty ; input rxclk ; input uld_rx_data ; output [7:0] rx_data ; input rx_enable ; input rx_in ; output rx_empty ; // Internal Variablesreg [7:0] tx_reg ; reg tx_empty ; reg tx_over_run ;reg [3:0] tx_cnt ;reg tx_out ;reg [7:0] rx_reg ;reg [7:0] rx_data ;reg [3:0] rx_sample_cnt ;reg [3:0] rx_cnt ;reg rx_frame_err ;reg rx_over_run ;reg rx_empty ;reg rx_d1 ;reg rx_d2 ;reg rx_busy ;// UART RX Logicalways @ (posedge rxclk or posedge reset) if (reset) beginrx_reg <= 0;rx_data <= 0;rx_sample_cnt <= 0;rx_cnt <= 0;rx_frame_err <= 0;rx_over_run <= 0;rx_empty <= 1;rx_d1 <= 1;rx_d2 <= 1;rx_busy <= 0;end else begin// Synchronize the asynch signalrx_d1 <= rx_in;rx_d2 <= rx_d1;// Uload the rx dataif (uld_rx_data) beginrx_data <= rx_reg;rx_empty <= 1;end// Receive data only when rx is enabledif (rx_enable) begin// Check if just received start of frameif (!rx_busy && !rx_d2) beginrx_busy <= 1;rx_sample_cnt <= 1;rx_cnt <= 0;end// Start of frame detected, Proceed with rest of data if (rx_busy) beginrx_sample_cnt <= rx_sample_cnt + 1;// Logic to sample at middle of dataif (rx_sample_cnt == 7) beginif ((rx_d2 == 1) && (rx_cnt == 0)) beginrx_busy <= 0;end else beginrx_cnt <= rx_cnt + 1;// Start storing the rx dataif (rx_cnt > 0 && rx_cnt < 9) beginrx_reg[rx_cnt - 1] <= rx_d2;endif (rx_cnt == 9) beginrx_busy <= 0;// Check if End of frame received correctlyif (rx_d2 == 0) beginrx_frame_err <= 1;end else beginrx_empty <= 0;rx_frame_err <= 0;// Check if last rx data was not unloaded,rx_over_run <= (rx_empty) ? 0 : 1;endendendendendendif (!rx_enable) beginrx_busy <= 0;endend// UART TX Logicalways @ (posedge txclk or posedge reset) if (reset) begintx_reg <= 0;tx_empty <= 1;tx_over_run <= 0;tx_out <= 1;tx_cnt <= 0;end else beginif (ld_tx_data) beginif (!tx_empty) begintx_over_run <= 0;end else begintx_reg <= tx_data;tx_empty <= 0;endendif (tx_enable && !tx_empty) begintx_cnt <= tx_cnt + 1;if (tx_cnt == 0) begintx_out <= 0;endif (tx_cnt > 0 && tx_cnt < 9) begintx_out <= tx_reg[tx_cnt -1];endif (tx_cnt == 9) begintx_out <= 1;tx_cnt <= 0;tx_empty <= 1;endendif (!tx_enable) begintx_cnt <= 0;endendendmodule摘要:掌握UART0配置及使用,程序中将UART0 配置到P0.0、P0.1。
RT-Thread设备驱动UART浅析OS版本:RT-Thread 4.0.0芯⽚:STM32F407RT-Thread的串⼝驱动框架与Linux相识,分成 I/O设备框架 + 设备底层驱动;1. serial设备初始化及使⽤将配置使能的 uart_obj[ ] 进⾏设备注册rtthread_startup --> rt_hw_usart_init() --> rt_hw_serial_register --> rt_device_register设备注册之后就可使⽤设备操作⽅式来使⽤串⼝rt_device_find("uart3") --> rt_device_open(serial, RT_DEVICE_FLAG_DMA_RX) --> rt_device_set_rx_indicate(serial, uart_dma_rx_handle)2. serial设备设备层 rt_device 注册及 ops 实现const static struct rt_device_ops serial_ops ={rt_serial_init,rt_serial_open,rt_serial_close,rt_serial_read,rt_serial_write,rt_serial_control};⽽serial设备 rt_serial_device 为 rt_device 的⼀个⼦类struct rt_serial_device{ struct rt_device parent; const struct rt_uart_ops *ops; struct serial_configure config; void *serial_rx; void *serial_tx;};其中 rt_serial_device 中的 ops 通过 stm32_uart_ops 实现,这样 rt_device、rt_serial_device 和 uart底层就都关联起来了硬件驱动层⽂件drv_usart.cdrv_usart.hstm32f4xx_hal_msp.c主要内容 ops 实现,中断处理static const struct rt_uart_ops stm32_uart_ops ={.configure = stm32_configure, //默认配置.control = stm32_control,.putc = stm32_putc,.getc = stm32_getc,};串⼝硬件初始化 HAL_UART_MspInit 对串⼝引脚和时钟的初始化在 stm32f4xx_hal_msp.c 中,通过配置CubeMX⽣成;3. 驱动分析serial 的 control 操作设计成不能设置中断,即缺少 RT_DEVICE_CTRL_SET_INT 和 RT_DEVICE_CTRL_CLR_INT 操作,这样可以避免误设置;同时也是由于串⼝接收已经设计成三选⼀⽅式:中断、DMA、轮询;说⼀下DMA,因为这也是我们最常⽤的串⼝数据接收处理⽅式;RTT的 serial 的DMA接收,采⽤的 IDLE 中断来控制DMA接收数据的,在 drv_usart.c 中static void stm32_dma_config(struct rt_serial_device *serial){....../* enable interrupt */__HAL_UART_ENABLE_IT(&(uart->handle), UART_IT_IDLE); //使能了空闲中断和DMA中断/* enable rx irq */HAL_NVIC_SetPriority(uart->config->dma_rx->dma_irq, 0, 0);HAL_NVIC_EnableIRQ(uart->config->dma_rx->dma_irq);HAL_NVIC_SetPriority(uart->config->irq_type, 1, 0);HAL_NVIC_EnableIRQ(uart->config->irq_type);....}在中断处理函数 uart_isr 中实现了static void uart_isr(struct rt_serial_device *serial){....#ifdef RT_SERIAL_USING_DMAelse if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET)) //IDLE空闲中断{level = rt_hw_interrupt_disable();recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma.handle));recv_len = recv_total_index - uart->st_index;uart->st_index = recv_total_index;rt_hw_interrupt_enable(level);if (recv_len){rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));}__HAL_UART_CLEAR_IDLEFLAG(&uart->handle);}#endif.....}void rt_hw_serial_isr(struct rt_serial_device *serial, int event){case RT_SERIAL_EVENT_RX_DMADONE:{int length;rt_base_t level;/* get DMA rx length */length = (event & (~0xff)) >> 8;if (serial->config.bufsz == 0){struct rt_serial_rx_dma* rx_dma;rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx;RT_ASSERT(rx_dma != RT_NULL);RT_ASSERT(serial->parent.rx_indicate != RT_NULL);serial->parent.rx_indicate(&(serial->parent), length);rx_dma->activated = RT_FALSE;}else{/* disable interrupt */level = rt_hw_interrupt_disable();/* update fifo put index */rt_dma_recv_update_put_index(serial, length);/* calculate received total length */length = rt_dma_calc_recved_len(serial);/* enable interrupt */rt_hw_interrupt_enable(level);/* invoke callback */if (serial->parent.rx_indicate != RT_NULL){serial->parent.rx_indicate(&(serial->parent), length); //应⽤回调接收处理函数}}break;}}如果进⾏⼤数据传输,发现默认缓存(64字节)不够⽤可修改 RT_SERIAL_RB_BUFSZ 值#define RT_SERIAL_CONFIG_DEFAULT \{ \BAUD_RATE_115200, /* 115200 bits/s */ \DATA_BITS_8, /* 8 databits */ \STOP_BITS_1, /* 1 stopbit */ \PARITY_NONE, /* No parity */ \BIT_ORDER_LSB, /* LSB first sent */ \NRZ_NORMAL, /* Normal mode */ \RT_SERIAL_RB_BUFSZ, /* Buffer size */ \0 \}#define RT_SERIAL_RB_BUFSZ 64。
串口通信实验报告基本实验:16位的乘法器设计思想:乘法器根据以往学过数电的设计经验,应该是移位相加的方法,设被乘数为[15:0]a,乘数为[15:0]b,则从b的最高位开始算起,c初值为0,为b最高位为1,则c就等于c+a;接下来,若b的次高位为1,则c左移一位加a,若为0则c左移一位就可以了,这样的步骤做到b的最低位那么c的值就是a*b,当然最好c是中间寄存器,这样结果才不会出现中间值。
实验的源码:module muti(clk,rst,ready,a,b,c);input clk;input rst;input [15:0]a;input [15:0]b;output [31:0]c;output ready;reg [31:0]c;reg ready;reg [31:0]temp;reg [5:0]n;always @(posedge clk or posedge rst)beginif(rst)beginc<=0;ready<=1;temp<=0;n<=32;endelseif(ready)begintemp<=0;n<=32;ready<=0;endelseif(n)beginif(b[n-1])begintemp<=(temp<<1)+a;n<=n-1;endbegintemp<=temp<<1;n<=n-1;endendelsebeginc<=temp;n<=32;ready<=1;endendendmodul测试代码:`timescale 1ns/1ns module tb;reg clk;reg [15:0]a;reg [15:0]b;reg rst;wire ready;wire [31:0]c;always #10 clk=~clk; initialbeginrst<=1;clk<=0;a=0;b=0;#10 rst=0;#21 a=21;b=32;#650 a=3;b=4;#700 $stop;endmuti muti_unit(.a(a),.b(b),.rst(rst),.clk(clk),.ready(ready),.c(c));endmodule这边a被乘数,b是乘数,当rst为高时,则将c置0,ready置一,ready信号为高表示此时空闲可以计算,rst为低时则开始计算,21*32为672,3*4为12,在乘法操作时,ready信号为低电平表示在工作中不能再输入进行计算,当计算结束则变为高电平。
UART驱动分析在linux⽤户层上要操作底层串⼝需要对/dev/ttySxxx操作,这⾥的ttySx指实际的终端串⼝。
以下以全志A64为实例,分析UART驱动以及浅谈TTY架构。
linux-3.10/drivers/tty/serial/sunxi-uart.c:1static const struct of_device_id sunxi_uart_match[] = {2 { .compatible = "allwinner,sun8i-uart", },3 { .compatible = "allwinner,sun50i-uart", },4 {},5 };6 MODULE_DEVICE_TABLE(of, sunxi_uart_match); //设备树查找device并注册78static struct uart_driver sw_uart_driver = {9 .owner = THIS_MODULE,10 .driver_name = SUNXI_UART_DEV_NAME, //"uart"11 .dev_name = "ttyS",12 .nr = SUNXI_UART_NUM, //613 .cons = SW_CONSOLE,14 };1516static int __init sunxi_uart_init(void)17 {18int ret;1920 ret = uart_register_driver(&sw_uart_driver); //注册tty_driver21if (unlikely(ret)) {22 SERIAL_MSG("driver initializied\n");23return ret;24 }2526return platform_driver_register(&sw_uport_platform_driver); //硬件平台相关相关进⼊probe27 }先看看注册tty_driver⾥⾯做了什么操作,删减部分代码linux-3.10/drivers/tty/serial/serial_core.c: 1static const struct tty_operations uart_ops = {2 .open = uart_open,3 .close = uart_close,4 .write = uart_write,5 .put_char = uart_put_char,6 .flush_chars = uart_flush_chars,7 .write_room = uart_write_room,8 .chars_in_buffer = uart_chars_in_buffer,9 .flush_buffer = uart_flush_buffer,10 .ioctl = uart_ioctl,11 .throttle = uart_throttle,12 .unthrottle = uart_unthrottle,13 .send_xchar = uart_send_xchar,14 .set_termios = uart_set_termios,15 .set_ldisc = uart_set_ldisc,16 .stop = uart_stop,17 .start = uart_start,18 .hangup = uart_hangup,19 .break_ctl = uart_break_ctl,20 .wait_until_sent = uart_wait_until_sent,21 #ifdef CONFIG_PROC_FS22 .proc_fops = &uart_proc_fops,23#endif24 .tiocmget = uart_tiocmget,25 .tiocmset = uart_tiocmset,26 .get_icount = uart_get_icount,27 #ifdef CONFIG_CONSOLE_POLL28 .poll_init = uart_poll_init,29 .poll_get_char = uart_poll_get_char,30 .poll_put_char = uart_poll_put_char,31#endif32 };3334int uart_register_driver(struct uart_driver *drv)35 {36struct tty_driver *normal;37int i, retval;3839 drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);4041 normal = alloc_tty_driver(drv->nr);43 drv->tty_driver = normal;4445 normal->driver_name = drv->driver_name;46 normal->name = drv->dev_name;47 normal->major = drv->major;48 normal->minor_start = drv->minor;49 normal->type = TTY_DRIVER_TYPE_SERIAL;50 normal->subtype = SERIAL_TYPE_NORMAL;51 normal->init_termios = tty_std_termios;52 normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;53 normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;54 normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;55 //TTY_DRIVER_DYNAMIC_DEV不⽴刻注册设备,只初始化56 normal->driver_state = drv;57 tty_set_operations(normal, &uart_ops); //normal->ops = uart_ops5859/*60 * Initialise the UART state(s).61*/62for (i = 0; i < drv->nr; i++) {63struct uart_state *state = drv->state + i;64struct tty_port *port = &state->port;65//初始化tty_port的等待队列open_wait,close_wait,delta_msr_wait和buffer⼯作队列flush_to_ldisc66 tty_port_init(port);6768 port->ops = &uart_port_ops; //tty_port->ops = uart_port_ops69 port->close_delay = HZ / 2; /* .5 seconds */70 port->closing_wait = 30 * HZ;/* 30 seconds */71 }7273 retval = tty_register_driver(normal); //初始化tty字符设备但不注册74return retval;以上完成tty的初始化,从以上代码可以知道tty_driver->ops = uart_ops,uart_ops是串⼝底层操作。
uart驱动电路设计摘要:一、uart驱动电路设计概述1.uart驱动电路的作用2.uart驱动电路的设计目标二、uart驱动电路设计原理1.uart通信的基本原理2.uart驱动电路的关键组件3.uart驱动电路的工作流程三、uart驱动电路设计步骤1.确定电路拓扑结构2.选择合适的元器件3.设计电路原理图4.布局与布线5.仿真与测试四、uart驱动电路设计实践1.基于FPGA的uart驱动电路设计2.基于ASIC的uart驱动电路设计3.常见问题与解决方案五、uart驱动电路设计展望1.新技术的发展趋势2.应用领域的拓展3.我国在uart驱动电路设计方面的优势与挑战正文:一、uart驱动电路设计概述uart驱动电路,即通用异步接收发送器(Universal Asynchronous Receiver/Transmitter)驱动电路,是一种用于实现串行通信的电路。
在电子设备中,uart驱动电路主要负责数据的接收与发送,其性能直接影响到通信的质量和效率。
因此,设计一个高性能、稳定的uart驱动电路是通信系统设计中的关键环节。
二、uart驱动电路设计原理1.uart通信的基本原理uart通信是一种异步通信方式,数据是按照位(bit)进行传输的。
发送方将数据从并行转换为串行,按位发送给接收方。
接收方收到串行数据后,再将其转换为并行数据。
在通信过程中,双方需要约定一个波特率(baud rate),即每秒传输的位速率。
2.uart驱动电路的关键组件uart驱动电路主要包括以下几个关键组件:(1)uart芯片:作为核心部件,负责数据的接收与发送。
(2)电平转换器:用于将uart芯片与外部逻辑电平进行转换,以保证通信的稳定性。
(3)波特率发生器:产生所需的波特率信号,用于同步数据传输。
(4)其他辅助元件:如电阻、电容、二极管等,用于组成电路的基本组成部分。
3.uart驱动电路的工作流程uart驱动电路的工作流程主要包括以下几个步骤:(1)数据接收:uart芯片接收来自其他设备的串行数据。
uart实验报告
1. 实验目的
本次实验的目的是验证串口通信协议UART的功能,通过USB-UART转换器控制开发板的板载LED的状态,以及通过调试软件UART通信观察调试台的信息输出。
2. 实验内容
本次实验环境是Espruino开发板,首先通过USB线将开发板连接至PC,使用一款USB-UART转换器将开发板连接至调试软件 PuTTY上,去UART连接口设置为9200,然后打开Espruino IDE软件,在终端上编写代码,不断编译及执行代码,以实现LED灯的转换状态。
具体实现步骤如下:
(1)安装Espruino IDE软件,建立编程环境,编写具体编程代码。
(3)将编写好的代码上传到Espruino,在调试软件 PuTTY 上可以看到板载 LED有明暗转换的视觉效果,从而验证Uart功能正常。
一:前言接着前面的终端控制台分析,接下来分析serial的驱动.在linux中,serial也对应着终端,通常被称为串口终端.在shell上,我们看到的/dev/ttyS*就是串口终端所对应的设备节点.在分析具体的serial驱动之前.有必要先分析uart驱动架构.uart是Universal Asynchronous Receiver and Transmitter的缩写.翻译成中文即为”通用异步收发器”.它是串口设备驱动的封装层.二:uart驱动架构概貌如下图所示:上图中红色部份标识即为uart部份的操作.从上图可以看到,uart设备是继tty_driver的又一层封装.实际上uart_driver就是对应tty_driver.在它的操作函数中,将操作转入uart_port.在写操作的时候,先将数据放入一个叫做circ_buf的环形缓存区.然后uart_port从缓存区中取数据,将其写入到串口设备中.当uart_port从serial设备接收到数据时,会将设备放入对应line discipline的缓存区中.这样.用户在编写串口驱动的时候,只先要注册一个uart_driver.它的主要作用是定义设备节点号.然后将对设备的各项操作封装在uart_port.驱动工程师没必要关心上层的流程,只需按硬件规范将uart_port中的接口函数完成就可以了.三:uart驱动中重要的数据结构及其关联我们可以自己考虑下,基于上面的架构代码应该要怎么写.首先考虑以下几点:1: 一个uart_driver通常会注册一段设备号.即在用户空间会看到uart_driver对应有多个设备节点.例如:/dev/ttyS0 /dev/ttyS1每个设备节点是对应一个具体硬件的,从上面的架构来看,每个设备文件应该对应一个uart_port.也就是说:uart_device怎么同多个uart_port关系起来?怎么去区分操作的是哪一个设备文件?2:每个uart_port对应一个circ_buf,所以uart_port必须要和这个缓存区关系起来回忆tty驱动架构中.tty_driver有一个叫成员指向一个数组,即tty->ttys.每个设备文件对应设数组中的一项.而这个数组所代码的数据结构为tty_struct. 相应的tty_struct会将tty_driver和ldisc关联起来.那在uart驱动中,是否也可用相同的方式来处理呢?将uart驱动常用的数据结构表示如下:结合上面提出的疑问.可以很清楚的看懂这些结构的设计.四:uart_driver的注册操作Uart_driver注册对应的函数为: uart_register_driver()代码如下:int uart_register_driver(struct uart_driver *drv){struct tty_driver *normal = NULL;int i, retval;BUG_ON(drv->state);/** Maybe we should be using a slab cache for this, especially if* we have a large number of ports to handle.*/drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);retval = -ENOMEM;if (!drv->state)goto out;normal = alloc_tty_driver(drv->nr);if (!normal)goto out;drv->tty_driver = normal;normal->owner = drv->owner;normal->driver_name = drv->driver_name;normal->name = drv->dev_name;normal->major = drv->major;normal->minor_start = drv->minor;normal->type = TTY_DRIVER_TYPE_SERIAL;normal->subtype = SERIAL_TYPE_NORMAL;normal->init_termios = tty_std_termios;normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; normal->driver_state = drv;tty_set_operations(normal, &uart_ops);/** Initialise the UART state(s).*/for (i = 0; i nr; i ) {struct uart_state *state = drv->state i;state->close_delay = 500; /* .5 seconds */state->closing_wait = 30000; /* 30 seconds */mutex_init(&state->mutex);}retval = tty_register_driver(normal);out:if (retvalput_tty_driver(normal);kfree(drv->state);}return retval;}从上面代码可以看出.uart_driver中很多数据结构其实就是tty_driver中的.将数据转换为tty_driver之后,注册tty_driver.然后初始化uart_driver->state 的存储空间.这样,就会注册uart_driver->nr个设备节点.主设备号为uart_driver-> major. 开始的次设备号为uart_driver-> minor.值得注意的是.在这里将tty_driver的操作集统一设为了uart_ops.其次,在tty_driver-> driver_state保存了这个uart_driver.这样做是为了在用户空间对设备文件的操作时,很容易转到对应的uart_driver.另外:tty_driver的flags成员值为: TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV.里面包含有TTY_DRIVER_DYNAMIC_DEV标志.结合之前对tty的分析.如果包含有这个标志,是不会在初始化的时候去注册device.也就是说在/dev/下没有动态生成结点(如果是/dev下静态创建了这个结点就另当别论了^_^).流程图如下:五: uart_add_one_port()操作在前面提到.在对uart设备文件过程中.会将操作转换到对应的port上,这个port跟uart_driver是怎么关联起来的呢?这就是uart_add_ont_port()的主要工作了.顾名思义,这个函数是在uart_driver增加一个port.代码如下:int uart_add_one_port(struct uart_driver *drv, struct uart_port *port){struct uart_state *state;int ret = 0;struct device *tty_dev;BUG_ON(in_interrupt());if (port->line >= drv->nr)return -EINVAL;state = drv->state port->line;mutex_lock(&port_mutex);mutex_lock(&state->mutex);if (state->port) {ret = -EINVAL;goto out;}state->port = port;state->pm_state = -1;port->cons = drv->cons;port->info = state->info;/** If this port is a console, then the spinlock is already* initialised.*/if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) { spin_lock_init(&port->lock);lockdep_set_class(&port->lock, &port_lock_key);}uart_configure_port(drv, state, port);/** Register the port whether it's detected or not. This allows* setserial to be used to alter this ports parameters.*/tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); if (likely(!IS_ERR(tty_dev))) {device_can_wakeup(tty_dev) = 1;device_set_wakeup_enable(tty_dev, 0);} elseprintk(KERN_ERR "Cannot register tty device on line %d\n",port->line);/** Ensure UPF_DEAD is not set.*/port->flags &= ~UPF_DEAD;out:mutex_unlock(&state->mutex);mutex_unlock(&port_mutex);return ret;}首先这个函数不能在中断环境中使用. Uart_port->line就是对uart设备文件序号.它对应的也就是uart_driver->state数组中的uart_port->line项.它主要初始化对应uart_driver->state项.接着调用uart_configure_port()进行port的自动配置.然后注册tty_device.如果用户空间运行了udev或者已经配置好了hotplug.就会在/dev下自动生成设备文件了.操作流程图如下所示:六:设备节点的open操作在用户空间执行open操作的时候,就会执行uart_ops->open. Uart_ops的定义如下: static const struct tty_operations uart_ops = {.open = uart_open,.close = uart_close,.write = uart_write,.put_char = uart_put_char,.flush_chars = uart_flush_chars,.write_room = uart_write_room,.chars_in_buffer= uart_chars_in_buffer,.flush_buffer = uart_flush_buffer,.ioctl = uart_ioctl,.throttle = uart_throttle,.unthrottle = uart_unthrottle,.send_xchar = uart_send_xchar,.set_termios = uart_set_termios,.stop = uart_stop,.start = uart_start,.hangup = uart_hangup,.break_ctl = uart_break_ctl,.wait_until_sent= uart_wait_until_sent,#ifdef CONFIG_PROC_FS.read_proc = uart_read_proc,#endif.tiocmget = uart_tiocmget,.tiocmset = uart_tiocmset,};对应open的操作接口为uart_open.代码如下:static int uart_open(struct tty_struct *tty, struct file *filp){struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;struct uart_state *state;int retval, line = tty->index;BUG_ON(!kernel_locked());pr_debug("uart_open(%d) called\n", line);/** tty->driver->num won't change, so we won't fail here with* tty->driver_data set to something non-NULL (and therefore* we won't get caught by uart_close()).*/retval = -ENODEV;if (line >= tty->driver->num)goto fail;/** We take the semaphore inside uart_get to guarantee that we won't* be re-entered while allocating the info structure, or while we* request any IRQs that the driver may need. This also has the nice* side-effect that it delays the action of uart_hangup, so we can* guarantee that info->tty will always contain something reasonable.*/state = uart_get(drv, line);if (IS_ERR(state)) {retval = PTR_ERR(state);goto fail;}/** Once we set tty->driver_data here, we are guaranteed that* uart_close() will decrement the driver module use count.* Any failures from here onwards should not touch the count.*/tty->driver_data = state;tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0;state->info->tty = tty;/** If the port is in the middle of closing, bail out now.*/if (tty_hung_up_p(filp)) {retval = -EAGAIN;state->count--;mutex_unlock(&state->mutex);goto fail;}/** Make sure the device is in D0 state.*/if (state->count == 1)uart_change_pm(state, 0);/** Start up the serial port.*/retval = uart_startup(state, 0);/** If we succeeded, wait until the port is ready.*/if (retval == 0)retval = uart_block_til_ready(filp, state);mutex_unlock(&state->mutex);/** If this is the first open to succeed, adjust things to suit.*/if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {state->info->flags |= UIF_NORMAL_ACTIVE;uart_update_termios(state);}fail:return retval;}在这里函数里,继续完成操作的设备文件所对应state初始化.现在用户空间open这个设备了.即要对这个文件进行操作了.那uart_port也要开始工作了.即调用uart_startup()使其进入工作状态.当然,也需要初始化uart_port所对应的环形缓冲区circ_buf.即state->info-> xmit.特别要注意,在这里将tty->driver_data = state;这是因为以后的操作只有port相关了,不需要去了解uart_driver的相关信息.跟踪看一下里面调用的两个重要的子函数. uart_get()和uart_startup().先分析uart_get().代码如下:static struct uart_state *uart_get(struct uart_driver *drv, int line) {struct uart_state *state;int ret = 0;state = drv->state line;if (mutex_lock_interruptible(&state->mutex)) {ret = -ERESTARTSYS;goto err;}state->count ;if (!state->port || state->port->flags & UPF_DEAD) {ret = -ENXIO;goto err_unlock;}if (!state->info) {state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL); if (state->info) {init_waitqueue_head(&state->info->open_wait);init_waitqueue_head(&state->info->delta_msr_wait);/** Link the info into the other structures.*/state->port->info = state->info;tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state);} else {ret = -ENOMEM;goto err_unlock;}}return state;err_unlock:state->count--;mutex_unlock(&state->mutex);err:return ERR_PTR(ret);}从代码中可以看出.这里注要是操作是初始化state->info.注意port->info就是state->info的一个副本.即port直接通过port->info可以找到它要操作的缓存区.uart_startup()代码如下:static int uart_startup(struct uart_state *state, int init_hw){struct uart_info *info = state->info;struct uart_port *port = state->port;unsigned long page;int retval = 0;if (info->flags & UIF_INITIALIZED)return 0;/** Set the TTY IO error marker - we will only clear this* once we have successfully opened the port. Also set* up the tty->alt_speed kludge*/set_bit(TTY_IO_ERROR, &info->tty->flags);if (port->type == PORT_UNKNOWN)return 0;/** Initialise and allocate the transmit and temporary* buffer.*/if (!info->xmit.buf) {page = get_zeroed_page(GFP_KERNEL);if (!page)return -ENOMEM;info->xmit.buf = (unsigned char *) page;uart_circ_clear(&info->xmit);}retval = port->ops->startup(port);if (retval == 0) {if (init_hw) {/** Initialise the hardware port settings.*/uart_change_speed(state, NULL);/** Setup the RTS and DTR signals once the* port is open and ready to respond.*/if (info->tty->termios->c_cflag & CBAUD)uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);}if (info->flags & UIF_CTS_FLOW) {spin_lock_irq(&port->lock);if (!(port->ops->get_mctrl(port) & TIOCM_CTS))info->tty->hw_stopped = 1;spin_unlock_irq(&port->lock);}info->flags |= UIF_INITIALIZED;clear_bit(TTY_IO_ERROR, &info->tty->flags);}if (retval && capable(CAP_SYS_ADMIN))retval = 0;return retval;}在这里,注要完成对环形缓冲,即info->xmit的初始化.然后调用port->ops->startup( )将这个port带入到工作状态.其它的是一个可调参数的设置,就不详细讲解了.七:设备节点的write操作Write操作对应的操作接口为uart_write( ).代码如下:static intuart_write(struct tty_struct *tty, const unsigned char *buf, int count){struct uart_state *state = tty->driver_data;struct uart_port *port;struct circ_buf *circ;unsigned long flags;int c, ret = 0;/** This means you called this function _after_ the port was* closed. No cookie for you.*/if (!state || !state->info) {WARN_ON(1);return -EL3HLT;}port = state->port;circ = &state->info->xmit;if (!circ->buf)return 0;spin_lock_irqsave(&port->lock, flags);while (1) {c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (countc = count;if (cbreak;memcpy(circ->buf circ->head, buf, c);circ->head = (circ->head c) & (UART_XMIT_SIZE - 1);buf = c;count -= c;ret = c;}spin_unlock_irqrestore(&port->lock, flags);uart_start(tty);return ret;}Uart_start()代码如下:static void uart_start(struct tty_struct *tty){struct uart_state *state = tty->driver_data;struct uart_port *port = state->port;unsigned long flags;spin_lock_irqsave(&port->lock, flags);__uart_start(tty);spin_unlock_irqrestore(&port->lock, flags);}static void __uart_start(struct tty_struct *tty){struct uart_state *state = tty->driver_data;struct uart_port *port = state->port;if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&!tty->stopped && !tty->hw_stopped)port->ops->start_tx(port);}显然,对于write操作而言,它就是将数据copy到环形缓存区.然后调用port->ops->start_tx()将数据写到硬件寄存器.八:Read操作Uart的read操作同Tty的read操作相同,即都是调用ldsic->read()读取read_buf中的内容.有对这部份内容不太清楚的,参阅>.九:小结本小节是分析serial驱动的基础.在理解了tty驱动架构之后,再来理解uart驱动架构应该不是很难.随着我们在linux设备驱动分析的深入,越来越深刻的体会到,linux的设备驱动架构很多都是相通的.只要深刻理解了一种驱动架构.举一反三.也就很容易分析出其它架构的驱动了.。
UART数据传送实验本实验利用ADSPBF533-EZ-KIT评估板的硬件资源,通过UART口进行数据传送实验。
UART 口用查询和中断两种方式数据传送是否完成。
实验结果可以通过主机上的串口调试软件可以看到传送的数据。
1.测试程序简介1)本实验使用的程序包括main.asm、Startup.asm、和Uartlib.asm3个模块,以及常数和宏定义、全局变量定义部分。
其中main.asm函数完成程序的控制,Uartlib.asm完成对UART 的初始化以及对子程序的定义,Startup.asm完成程序的启动设置。
2)程序工作流程:main.asm首先初始化UART(设置各个控制寄存器),初始化UART_GCTL寄存器始能UART时钟,然后对UART_LCR寄存器进行初始化将UART 设置成8位数据位、1位停止位、无奇偶校验位,从UART_DLL、UART_DLH中读出Divisor的值并计算出周期。
调用设置UART的波特率函数uart_autobaud,波特率设成自动波特率。
程序进入以查询和中断方式传送数据。
本实验的程序位于…\ UART子目录,打开工程文件UART.DPJ,可以看到演示软件主程序模块:main.asm#include <defBF533.h>#include "uartlib.h"#include "sicnames.h"/***************************************************************************** ** Example requires a simple hello-world string.* Initialize on-chip SRAM at boot time accordingly.*****************************************************************************/.section data1;.byte sHead[] = 13,10,'-------------------------------',13,10,'ADSP-BF533 Blackfin is speaking',13,10,'(DL =',0;.byte sTail[] = ')',13,10,'-------------------------------',13,10,0;.byte sEcho[] = 'Type any character. The Blackfin UART', 13,10,'returns the echo> ',0;.align 4;.var aEchoFifo[16];/***************************************************************************** ** Main program starts here!*****************************************************************************/.section L1_code;.global _main;_main:[--sp] = rets;/***************************************************************************** ** First of all, initialize p0 to UART_GCTL register address.* p0 must not be changed within this example*****************************************************************************/p0.l = lo(UART_GCTL);p0.h = hi(UART_GCTL);call uart_autobaud;/***************************************************************************** ** r0 holds the timer period value, now.* Apply formula DL = PERIOD / (16 x 8 bits) and call uart_init that writes* the result to the two 8-bit DL registers (DLH:DLL).*****************************************************************************/ r0 >>= 7;call uart_init;/***************************************************************************** ** Transmit a Hello World string and the content of the DL registers.*****************************************************************************/p1.l = sHead;p1.h = sHead;call uart_puts;/***************************************************************************** ** Note that r0 still contains the DLH:DLL value*****************************************************************************/call uart_putreg;p1.l = sTail;p1.h = sTail;call uart_puts;/***************************************************************************** ** Wait until operation has finished completely. This is optional.*****************************************************************************/ call uart_wait4temt;/***************************************************************************** ** Transmit another string, but use interrupt mode, this time.* First TX interrupt channel must be assigned and enabled.* Assign to EVT9.** p3 points to the NULL-terminated string.** Important: if you enable all three UART interrupts channels, it is* recommended to keep this order of priorities:* PRIORITY(Error) >= PRIORITY(RX) >= PRIORITY(TX)*****************************************************************************/p1.l = lo(IMASK);p1.h = hi(IMASK);p3.l = sEcho;p3.h = sEcho;/***************************************************************************** ** Register TX service routine at EVT9.*****************************************************************************/r0.l = isr_uart_tx;r0.h = isr_uart_tx;[p1 + EVT9 - IMASK] = r0;/***************************************************************************** ** Unmask EVT9 interrupt.*****************************************************************************/r0 = [p1 + IMASK - IMASK];bitset(r0, bitpos(EVT_IVG9));[p1 + IMASK - IMASK] = r0;/***************************************************************************** ** Enable UART TX interrupt and assign it to EVT9.* Constants used below are defined in "sicnames.h" header.*****************************************************************************/p1.l = lo(SIC_IMASK);p1.h = hi(SIC_IMASK);r0.l = lo(IVG_SPT0_ERROR(15) | IVG_PPI_ERROR(15) | IVG_DMA_ERROR(15) | IVG_PLL_WAKEUP(15));r0.h = hi(IVG_RTC(15) | IVG_UART_ERROR(15) | IVG_SPT1_ERROR(15) | IVG_SPI_ERROR(15));[p1 + SIC_IAR0 - SIC_IMASK] = r0;r0.l = lo(IVG_SPT1_RX(15) | IVG_SPT0_TX(15) | IVG_SPT0_RX(15) |IVG_PPI(15));r0.h = hi(IVG_UART_TX( 9) | IVG_UART_RX(16) | IVG_SPI(15) | IVG_SPT1_TX(15));[p1 + SIC_IAR1 - SIC_IMASK] = r0;r0.l = lo(IVG_PFA(15) | IVG_TIMER2(15) | IVG_TIMER1(15) | IVG_TIMER0(15));r0.h = hi(IVG_SWDT(15) | IVG_MEMDMA1(15) | IVG_MEMDMA0(15) | IVG_PFB(15));[p1 + SIC_IAR2 - SIC_IMASK] = r0;r0.l = lo(IRQ_UART_TX);r0.h = hi(IRQ_UART_TX);[p1 + SIC_IMASK - SIC_IMASK] = r0;/***************************************************************************** ** Enable Interrupt Nesting.*****************************************************************************/[--sp] = reti;/***************************************************************************** ** Finally enable interrupts inside UART module, by setting proper bits* in the IER register. It is good programming style to clear potential* UART interrupt latches in advance, by reading RBR, LSR and IIR.** Setting the ETBEI bit automatically fires a TX interrupt request.*****************************************************************************/r0 = w[p0+UART_RBR-UART_GCTL] (z);r0 = w[p0+UART_LSR-UART_GCTL] (z);r0 = w[p0+UART_IIR-UART_GCTL] (z);r0 = ETBEI;w[p0+UART_IER-UART_GCTL] = r0;/******************************************************************************* Wait until operation has finished completely. Again, this is optional.*****************************************************************************/ call uart_wait4temt;/***************************************************************************** ** Disable UART TX interrupt again, and enable UART RX interrupt and UART* Line Error Interrupt.* Simply echo all received characters back to TX.** Disable nesting during the setup.*****************************************************************************/ reti = [sp++];p1.l = lo(IMASK);p1.h = hi(IMASK);/***************************************************************************** ** i0 and i1 are used to implement a little FIFO*****************************************************************************/i0.l = aEchoFifo;i0.h = aEchoFifo;i1 = i0;b0 = i0;b1 = i0;l0 = length(aEchoFifo);l1 = l0;/***************************************************************************** ** Register RX service routine at EVT8 and error routine to EVT7.*****************************************************************************/r0.l = isr_uart_error;r0.h = isr_uart_error;[p1 + EVT7 - IMASK] = r0;r0.l = isr_uart_rx;r0.h = isr_uart_rx;[p1 + EVT8 - IMASK] = r0;/***************************************************************************** ** Mask EVT9 interrupt and unmask EVT7 and EVT8.*****************************************************************************/r0 = [p1 + IMASK - IMASK];bitclr(r0, bitpos(EVT_IVG9));bitset(r0, bitpos(EVT_IVG7));bitset(r0, bitpos(EVT_IVG8));[p1 + IMASK - IMASK] = r0;/***************************************************************************** ** Enable and assign interrupts.*****************************************************************************/p1.l = lo(SIC_IMASK);p1.h = hi(SIC_IMASK);r0.l = lo(IVG_SPT0_ERROR(15) | IVG_PPI_ERROR(15) | IVG_DMA_ERROR(15) | IVG_PLL_WAKEUP(15));r0.h = hi(IVG_RTC(15) | IVG_UART_ERROR( 7) | IVG_SPT1_ERROR(15) | IVG_SPI_ERROR(15));[p1 + SIC_IAR0 - SIC_IMASK] = r0;r0.l = lo(IVG_SPT1_RX(15) | IVG_SPT0_TX(15) | IVG_SPT0_RX(15) | IVG_PPI(15));r0.h = hi(IVG_UART_TX(15) | IVG_UART_RX( 8) | IVG_SPI(15) | IVG_SPT1_TX(15));[p1 + SIC_IAR1 - SIC_IMASK] = r0;r0.l = lo(IVG_PFA(15) | IVG_TIMER2(15) | IVG_TIMER1(15) | IVG_TIMER0(15));r0.h = hi(IVG_SWDT(15) | IVG_MEMDMA1(15) | IVG_MEMDMA0(15) | IVG_PFB(15));[p1 + SIC_IAR2 - SIC_IMASK] = r0;r0.l = lo(IRQ_UART_RX | IRQ_UART_ERROR);r0.h = hi(IRQ_UART_RX | IRQ_UART_ERROR);[p1 + SIC_IMASK - SIC_IMASK] = r0;/***************************************************************************** ** Enable Interrupt Nesting.*****************************************************************************/ [--sp] = reti;/***************************************************************************** ** Finally enable interrupts inside UART module, by setting proper bits* in the IER register. It is good programming style to clear potential* UART interrupt latches in advance, by reading RBR, LSR and IIR.*****************************************************************************/r0 = w[p0+UART_RBR-UART_GCTL] (z);r0 = w[p0+UART_LSR-UART_GCTL] (z);r0 = w[p0+UART_IIR-UART_GCTL] (z);r0 = ELSI | ERBFI;w[p0+UART_IER-UART_GCTL] = r0;/***************************************************************************** ** The following endless loop tests whether data is ready in the aEchoFifo* and trasnmits it, if required. The FIFO is filled by the UART RX ISR.*****************************************************************************/ echo:r0 = i0;r1 = i1;CC = r0 == r1;if CC jump echo;r0 = [i1++];call uart_putc;jump echo;_main.end: nop;/***************************************************************************** ** UART TX Interrupt Service Rouine.** Load next byte from p3 pointer and moves it to THR register until* p3 points to NULL character. Note that a write to THR clears the* TX interrupt request.*****************************************************************************/ isr_uart_tx:[--sp] = r0;r0 = b[p3++] (z);CC = r0 == 0;if CC jump isr_tx_done;w[p0+UART_THR-UART_GCTL] = r0;r0 = [sp++];ssync;rti;/***************************************************************************** ** Once a NULL character was detected, the transmission should stop.* There is a need to clear the TX interrupt request. Since we don't* want to write to THR again, we can clear the request by reading the* IIR register. Note that system design needs to ensure that neigher* an UART RX or Line Status interrupt should be pending!** Note the double ssync instruction, required due to system latencies.*****************************************************************************/ isr_tx_done:r0 = w[p0+UART_IIR-UART_GCTL] (z);r0 = [sp++];ssync;ssync;rti;isr_uart_tx.end:/***************************************************************************** ** UART RX Interrupt Service Rouine.** When new data is received this ISR puts the new data into the aEchoFifo.* Special handling is required for carriage return and backspace.** Note that reading RBR clears the interrupt request.*****************************************************************************/ isr_uart_rx:[--sp] = r0;[--sp] = r1;r0 = w[p0+UART_RBR-UART_GCTL] (z);[i0++] = r0;r1 = 8; // backspaceCC = r0 == r1;if !CC jump isr10;r1 = 32 (z); // blank[i0++] = r1;r1 = 8 (z); // another backspace[i0++] = r1;isr10:r1 = 13; // carriage returnCC = r0 == r1;if !CC jump isr20;r1 = 10 (z); // line feed[i0++] = r1;isr20:r1 = [sp++];r0 = [sp++];ssync;rti;isr_uart_rx.end:/***************************************************************************** ** UART Error/Line Status Interrupt Service Rouine.** If an error is reported by the UART Line Status, then this ISR reads* the LSR register to determine the cause of the error (and to clear the* interrupt request).** To indicate the error a 0xFF character will be written to the FIFO.*****************************************************************************/ isr_uart_error:[--sp] = r0;r0 = w[p0+UART_LSR-UART_GCTL] (z);r0 = 0xFF (z);[i0++] = r0;r0 = [sp++];ssync;rti;isr_uart_error.end:2. 实验步骤1)连接硬件关闭PC机的电源,按照硬件连接图正确连接各个硬件设备,检查EZ-KIT板上的跳线位置是否正确,按照硬件连接图检查确保正确连接各个硬件设备。
实验四UART串口通信学院:研究生院学号:1400030034姓名:张秋明一、实验目得及要求设计一个UART串口通信协议,实现“串<-->并”转换功能得电路,也就就是“通用异步收发器”。
二、实验原理UART就是一种通用串行数据总线,用于异步通信。
该总线双向通信,可以实现全双工传输与接收。
在嵌入式设计中,UART用来主机与辅助设备通信,如汽车音响与外接AP之间得通信,与PC机通信包括与监控调试器与其它器件,如EEPROM通信。
UART作为异步串口通信协议得一种,工作原理就是将传输数据得每个字符一位接一位地传输。
其中各位得意义如下:起始位:先发出一个逻辑”0”得信号,表示传输字符得开始。
资料位:紧接着起始位之后。
资料位得个数可以就是4.5.6.7、8等,构成一个字符。
通常采用ASCII码。
从最低位开始传送,靠时钟定位。
奇偶校验位:资料位加上这一位后,使得“1”得位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送得正确性。
停止位:它就是一个字符数据得结束标志。
可以就是1位、1.5位、2位得高电平。
由于数据就是在传输线上定时得,并且每一个设备有其自己得时钟,很可能在通信中两台设备间出现了小小得不同步。
因此停止位不仅仅就是表示传输得结束,并且提供计算机校正时钟同步得机会。
适用于停止位得位数越多,不同时钟同步得容忍程度越大,但就是数据传输率同时也越慢。
空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。
波特率:就是衡量资料传送速率得指标。
表示每秒钟传送得符号数(symbol)。
一个符号代表得信息量(比特数)与符号得阶数有关。
例如资料传送速率为120字符/秒,传输使用256阶符号,每个符号代表8bit,则波特率就就是120baud,比特率就是120*8=960bit/s。
这两者得概念很容易搞错。
三、实现程序library ieee;use ieee、std_logic_1164.all;use ieee、std_logic_arith、all;use ieee、std_logic_unsigned、all;entity uart isport(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号rs232_rx: in std_logic; --RS232接收数据信号;rs232_tx: out std_logic --RS232发送数据信号;); end uart;architecture behav of uart isponent uart_rx port(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号rs232_rx: in std_logic; --RS232接收数据信号clk_bps: in std_logic; --此时clk_bps得高电平为接收数据得采样点bps_start:out std_logic; --接收到数据后,波特率时钟启动置位rx_data: out std_logic_vector(7 downto 0); --接收数据寄存器,保存直至下一个数据来到rx_int: out std_logic --接收数据中断信号,接收数据期间时钟为高电平,传送给串口发送);end ponent;ponent speed_select port(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号clk_bps: out std_logic; --此时clk_bps 得高电平为接收或者发送数据位得中间采样点bps_start:in std_logic --接收数据后,波特率时钟启动信号置位);end ponent;ponent uart_tx port(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号rs232_tx: out std_logic; --RS232接收数据信号clk_bps: in std_logic; --此时clk_bps 得高电平为接收数据得采样点bps_start:out std_logic; --接收到数据后,波特率时钟启动置位rx_data: in std_logic_vector(7 downto 0); --接收数据寄存器,保存直至下一个数据来到rx_int: in std_logic --接收数据中断信号,接收数据期间时钟为高电平,传送给串口发送模块,使得串口正在进行接收数据得时候,发送模块不工作,避免了一个完整得数据(1位起始位、8位数据位、1位停止位)还没有接收完全时,发送模块就已经将不正确得数据传输出去);end ponent;signal bps_start_1:std_logic;signal bps_start_2:std_logic;signal clk_bps_1:std_logic;signal clk_bps_2:std_logic;signal rx_data:std_logic_vector(7 downto 0);signal rx_int:std_logic;beginRX_TOP: uart_rx port map(clk=>clk,rst_n=>rst_n,rs232_rx=>rs232_rx,clk_bps=>clk_bps_1,bps_start=>bps_start_1,rx_data=>rx_data,rx_int=>rx_int);SPEED_TOP_RX: speed_select port map(clk=>clk,rst_n=>rst_n,clk_bps=>clk_bps_1,bps_start=>bps_start_1);TX_TOP:uart_tx port map(clk=>clk, --系统时钟rst_n=>rst_n, --复位信号rs232_tx=>rs232_tx, --RS232发送数据信号clk_bps=>clk_bps_2, --此时clk_bps 得高电平为发送数据得采样点bps_start=>bps_start_2, --接收到数据后,波特率时钟启动置位rx_data=>rx_data, --接收数据寄存器,保存直至下一个数据来到rx_int=>rx_int --接收数据中断信号,接收数据期间时钟为高电平,传送给串口发送模块,使得串口正在进行接收数据得时候,发送模块不工作,避免了一个完整得数据(1位起始位、8位数据位、1位停止位)还没有接收完全时,发送模块就已经将不正确得数据传输出去);SPEED_TOP_TX: speed_select port map(clk=>clk,rst_n=>rst_n,clk_bps=>clk_bps_2,bps_start=>bps_start_2);end behav;-----------------------------------------------------------------------------------------------------------------------3个子模块------------------------------------------------------------------------------异步接收模块-------------------------------------------library ieee;use ieee、std_logic_1164.all;use ieee、std_logic_unsigned、all;entity uart_rx isport(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号rs232_rx: in std_logic; --RS232接收数据信号clk_bps: in std_logic; --此时clk_bps得高电平为接收数据得采样点bps_start:out std_logic; --接收到数据后,波特率时钟启动置位rx_data: out std_logic_vector(7 downto 0); --接收数据寄存器,保存直至下一个数据来到rx_int: out std_logic --接收数据中断信号,接收数据期间时钟为高电平,传送给串口发送模块,使得串口正在进行接收数据得时候,发送模块不工作,避免了一个完整得数据(1位起始位、8位数据位、1位停止位)还没有接收完全时,发送模块就已经将不正确得数据传输出去);end uart_rx;architecture behav of uart_rx issignal rs232_rx0: std_logic;signal rs232_rx1: std_logic;signal rs232_rx2: std_logic;signal rs232_rx3: std_logic;signal neg_rs232_rx:std_logic;signal bps_start_r:std_logic;signal num:integer;signal rx_data_r:std_logic_vector(7 downto 0); --串口接收数据寄存器,保存直至下一个数据到来beginprocess(clk,rst_n)beginif (rst_n='0')thenrs232_rx0<='0';rs232_rx1<='0';rs232_rx2<='0';rs232_rx3<='0';elseif (rising_edge(clk)) thenrs232_rx0<=rs232_rx;rs232_rx1<=rs232_rx0;rs232_rx2<=rs232_rx1;rs232_rx3<=rs232_rx2;end if;end if;neg_rs232_rx <=rs232_rx3 and rs232_rx2 and not(rs232_rx1)and not(rs232_rx0);end process;process(clk,rst_n)beginif (rst_n='0')thenbps_start_r<='0';rx_int<='0';elseif (rising_edge(clk)) thenif(neg_rs232_rx='1') then --接收到串口数据线rs232_rx 得下降沿标志信号bps_start_r<='1'; --启动串口准备数据接收rx_int<='1'; --接收数据中断信号使能else if((num= 15) and (clk_bps='1')) then --接收完有用数据信息bps_start_r<='0'; --数据接收完毕,释放波特率启动信号rx_int<='0'; --接收数据中断信号关闭end if;end if;end if;end if;bps_start<=bps_start_r;end process;process(clk,rst_n)beginif (rst_n='0')thenrx_data_r<="00000000";rx_data<="00000000";num<=0;elseif (rising_edge(clk)) thenif(clk_bps='1')thennum<=num+1;case num iswhen 1=>rx_data_r(0)<=rs232_rx;--锁存第0bitwhen 2=>rx_data_r(1)<=rs232_rx;--锁存第0bitwhen 3=>rx_data_r(2)<=rs232_rx;--锁存第0bitwhen 4=>rx_data_r(3)<=rs232_rx;--锁存第0bitwhen 5=>rx_data_r(4)<=rs232_rx;--锁存第0bitwhen 6=>rx_data_r(5)<=rs232_rx;--锁存第0bitwhen 7=>rx_data_r(6)<=rs232_rx;--锁存第0bitwhen 8=>rx_data_r(7)<=rs232_rx;--锁存第0bitwhen 10=>rx_data<=rx_data_r;when 11=>num<=15;when others=>null;end case;if(num=15) thennum<=0;end if;end if;end if;end if;end process;end behav;---------------------------------波特率控制模块-----------------------------------------library ieee;use ieee、std_logic_1164.all;use ieee、std_logic_arith、all;use ieee、std_logic_unsigned、all;entity speed_select isport(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号clk_bps: out std_logic; --此时clk_bps得高电平为接收或者发送数据位得中间采样点bps_start:in std_logic --接收数据后,波特率时钟启动信号置位或者开始发送数据时,波特率时钟启动信号置位);end speed_select;architecture behav of speed_select issignal cnt:std_logic_vector(12 downto 0);signal clk_bps_r:std_logic;constant BPS_PARA:integer:=5207;constant BPS_PARA_2:integer:=2603;beginprocess(clk,rst_n)beginif (rst_n='0')thencnt<="00";elseif (rising_edge(clk)) thenif((cnt=BPS_PARA)or(bps_start='0')) thencnt<="00"; --波特率计数器清零elsecnt<=cnt+'1'; --波特率时钟计数启动end if;end if;end if;end process;process(clk,rst_n)beginif (rst_n='0')thenclk_bps_r<='0';elseif (rising_edge(clk)) thenif(cnt=BPS_PARA_2) thenclk_bps_r<='1'; --clk_bps_r高电平为接收数据位得中间采样点,同时也作为发送数据得数据改变点elseclk_bps_r<='0'; --波特率计数器清零end if;end if;end if;clk_bps<=clk_bps_r;end process;end behav;---------------------------------异步发送模块------------------------------------------- library ieee;use ieee、std_logic_1164.all;use ieee、std_logic_unsigned、all;entity uart_tx isport(clk : in std_logic; --系统时钟rst_n: in std_logic; --复位信号rs232_tx: out std_logic; --RS232接收数据信号clk_bps: in std_logic; --此时clk_bps得高电平为接收数据得采样点bps_start:out std_logic; --接收到数据后,波特率时钟启动置位rx_data: in std_logic_vector(7 downto 0); --接收数据寄存器,保存直至下一个数据来到rx_int: in std_logic --接收数据中断信号,接收数据期间时钟为高电平,传送给串口发送模块,使得串口正在进行接收数据得时候,发送模块不工作,避免了一个完整得数据(1位起始位、8位数据位、1位停止位)还没有接收完全时,发送模块就已经将不正确得数据传输出去);end uart_tx;architecture behav of uart_tx issignal rx_int0: std_logic;signal rx_int1: std_logic;signal rx_int2: std_logic;signal neg_rx_int:std_logic;signal bps_start_r:std_logic;signal num:integer;signal tx_data:std_logic_vector(7 downto 0); --串口接收数据寄存器,保存直至下一个数据到来beginprocess(clk,rst_n)beginif (rst_n='0')thenrx_int0<='0';rx_int1<='0';rx_int2<='0';elseif (rising_edge(clk)) thenrx_int0<=rx_int;rx_int1<=rx_int0;rx_int2<=rx_int1;end if;end if;neg_rx_int <=not(rx_int1)and (rx_int2);end process;process(clk,rst_n)beginif (rst_n='0')thenbps_start_r<='0';tx_data<="00000000";elseif (rising_edge(clk)) thenif(neg_rx_int='1') then --接收到串口数据线rs232_rx得下降沿标志信号bps_start_r<='1'; --启动串口准备数据接收tx_data<=rx_data; --接收数据中断信号使能else if((num= 15) and (clk_bps='1')) then --接收完有用数据信息bps_start_r<='0'; --数据接收完毕,释放波特率启动信号end if;end if;end if;end if;bps_start<=bps_start_r;end process;process(clk,rst_n)beginif (rst_n='0')thenrs232_tx<='1';num<=0;elseif (rising_edge(clk)) thenif(clk_bps='1')thennum<=num+1;case num iswhen 1=>rs232_tx<='0';when 2=>rs232_tx<=tx_data(0);--发送第1bitwhen 3=>rs232_tx<=tx_data(1);--发送第2bitwhen 4=>rs232_tx<=tx_data(2);--发送第3bitwhen 5=>rs232_tx<=tx_data(3);--发送第4bitwhen 6=>rs232_tx<=tx_data(4);--发送第5bitwhen 7=>rs232_tx<=tx_data(5);--发送第6bitwhen 8=>rs232_tx<=tx_data(6);--发送第7bitwhen 9=>rs232_tx<=tx_data(7);--发送第8bitwhen 10=>rs232_tx<='1';when 11=>num<=15;when others=>null;end case;if(num=15) thennum<=0;end if;end if;end if;end if;end process;end behav;四、实验步骤1.建立新工程UART,选择芯片,型号为cyclone ii EP2C35F484C8。
实验10-1 UART驱动程序分析实验【实验目的】掌握串口的通信原理。
熟悉串口驱动程序的结构。
【实验步骤】PXA UART串口驱动初始化第一步:打开linux-2.6.22.10\drivers\serial\pxa.c,找到串口驱动初始化serial_pxa_init(void)int __init serial_pxa_init(void){int ret;ret = uart_register_driver(&serial_pxa_reg);if (ret != 0)return ret;ret = platform_driver_register(&serial_pxa_driver);if (ret != 0)uart_unregister_driver(&serial_pxa_reg);return ret;}在UART初始化函数中,首先调用uart_register_driver(struct uart_driver *drv)函数来注册UART驱动,在这函数里它会分配一个tty_driver对象,并初始化tty_operations为serial_pxa_pops, 这是serial-core.c提供的统一的UART操作函数。
第二步:在linux-2.6.22.10\drivers\serial\pxa.c找到serial_pxa_reg结构体的定义如下:static struct uart_driver serial_pxa_reg = {.owner = THIS_MODULE,.driver_name = "PXA serial",.dev_name = "ttyS",.major = TTY_MAJOR,.minor = 64,.nr = ARRAY_SIZE(serial_pxa_ports),.cons = PXA_CONSOLE,};从serial_pxa_reg结构体中,UART使用的设备名为ttySn(n=0~3),主设备号major为4,次设备号minor为64,串口数nr=ARRAY_SIZE(serial_pxa_ports),根据serial_pxa_ports 结构体的定义,可以算出串口数nr =4,当然PXA270只有三个串口(FFUART、BTUART、STUART),但是PXA255/26x 微处理器还附加了HWUART,serial_pxa_ports定义如下;static struct uart_pxa_port serial_pxa_ports[] = {{ /* FFUART */.name = "FFUART",.cken = CKEN_FFUART,.port = {.type = PORT_PXA,.iotype = UPIO_MEM,.membase = (void *)&FFUART,.mapbase = __PREG(FFUART),.irq = IRQ_FFUART,.uartclk = 921600 * 16,.fifosize = 64,.ops = &serial_pxa_pops,.line = 0,},}, { /* BTUART */.name = "BTUART",.cken = CKEN_BTUART,.port = {.type = PORT_PXA,.iotype = UPIO_MEM,.membase = (void *)&BTUART,.mapbase = __PREG(BTUART),.irq = IRQ_BTUART,.uartclk = 921600 * 16,.fifosize = 64,.ops = &serial_pxa_pops,.line = 1,},}, { /* STUART */.name = "STUART",.cken = CKEN_STUART,.port = {.type = PORT_PXA,.iotype = UPIO_MEM,.membase = (void *)&STUART,.mapbase = __PREG(STUART),.irq = IRQ_STUART,.uartclk = 921600 * 16,.fifosize = 64,.ops = &serial_pxa_pops,.line = 2,},}, { /* HWUART */.name = "HWUART",.cken = CKEN_HWUART,.port = {.type = PORT_PXA,.iotype = UPIO_MEM,.membase = (void *)&HWUART,.mapbase = __PREG(HWUART),.irq = IRQ_HWUART,.uartclk = 921600 * 16,.fifosize = 64,.ops = &serial_pxa_pops,.line = 3,},}};第三步:UART平台驱动注册初始化函数然后调用平台驱动注册函数platform_driver_register(struct platform_driver *drv)注册UART驱动,该函数实际上定义有关“伪总线”(pseudo-bus)驱动的相关操作,Linux-2.6.x内核为简化一些片上系统(SoC)系统的集成外设和传统PC接插件的驱动接口,提出了“伪总线”的概念。
打开linux-2.6.22.10\drivers\Base\ Platform.c文件,找到 platform_driver_register函数:int platform_driver_register(struct platform_driver *drv){drv->driver.bus = &platform_bus_type;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;if (drv->suspend)drv->driver.suspend = platform_drv_suspend;if (drv->resume)drv->driver.resume = platform_drv_resume;return driver_register(&drv->driver);}EXPORT_SYMBOL_GPL(platform_driver_register);platform_driver_register函数中的serial_pxa_driver变量定义如下:static struct platform_driver serial_pxa_driver = {.probe = serial_pxa_probe,.remove = serial_pxa_remove,.suspend = serial_pxa_suspend,.resume = serial_pxa_resume,.driver = {.name = "pxa2xx-uart",},};该结构体实际上定义驱动探测(probe)、移除(remove)、挂起(suspend)、重启(resume)等相关函数操作:\drivers\serial\pxa.c文件本质上完成对所有这些函数的实现。
UART驱动相关操作函数第四步: UART驱动侦测函数(probe)static int serial_pxa_probe(struct platform_device *dev){serial_pxa_ports[dev->id].port.dev = &dev->dev;uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[dev->id].port);platform_set_drvdata(dev, &serial_pxa_ports[dev->id]);return 0;}UART驱动侦测函数serial_pxa_probe,首先获取基于平台设备的相关资源,然后调用uart_add_one_port函数实现将驱动和一个实际的端口结构体相关联,然后调用platform_set_drvdata函数将UART驱动添加到平台设备platform_device *dev中。
第五步:UART驱动卸载函数(remove)static int serial_pxa_remove(struct platform_device *dev){struct uart_pxa_port *sport = platform_get_drvdata(dev);platform_set_drvdata(dev, NULL);if (sport)uart_remove_one_port(&serial_pxa_reg, &sport->port);return 0;}UART驱动卸载函数与UART驱动侦测函数恰好相反,首先利用platform_get_drvdata 函数获取驱动相关数据,如果驱动存在,则利用uart_remove_one_port函数将驱动和实际端口结构体断开连接。
一般对于UART设备,这种情况只有在关闭操作系统时才存在。
第六步:UART驱动的挂起与重启(suspend、resume)UART驱动的挂起与重启在具有电源管理功能的系统中才可能发生,当系统进入低功耗或休眠状态,系统将调用serial_pxa_suspend函数使设备进入挂起状态,并将驱动的电源状态切换低功耗状态(3),如果系统被唤醒,则调用驱动的重启serial_pxa_resume,同时将驱动的电源状态切换到全功耗状态(0)。
static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state){struct uart_pxa_port *sport = platform_get_drvdata(dev);if (sport)uart_suspend_port(&serial_pxa_reg, &sport->port);return 0;}static int serial_pxa_resume(struct platform_device *dev){struct uart_pxa_port *sport = platform_get_drvdata(dev);if (sport)uart_resume_port(&serial_pxa_reg, &sport->port);return 0;}UART驱动操作在PXA架构的UART驱动侦测函数(probe)调用uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[dev->id].port)完成将驱动和一个实际的端口结构体相关联时,引用了使用serial_pxa_ports[dev->id].port,而在uart_pxa_port结构体中的port成员变量中,嵌套了一个uart_ops 结构体成员变量ops;,它定义了串口硬件能完成的所有操作(如数据的收发等)。