【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子Linux感兴趣的同学可以加群讨论:935446741
4)关注正点原子公众号,获取最新资料更新
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第1张图片

第二十一章 UART串口通信实验

不管是单片机开发还是嵌入式Linux开发,串口都是最常用到的外设。可以通过串口将开发板与电脑相连,然后在电脑上通过串口调试助手来调试程序。还有很多的模块,比如蓝牙、GPS、GPRS等都使用的串口来与主控进行通信的,在嵌入式Linux中一般使用串口作为控制台,所以掌握串口是必备的技能。本章我们就来学习如何驱动I.MX6U上的串口,并使用串口和电脑进行通信。

21.1 I.MX6U串口简介
21.1.1 UART简介
1、UART通信格式
串口全称叫做串行接口,通常也叫做COM接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。I.MX6U自带的UART外设就是串口的一种,UART全称是Universal Asynchronous Receiver/Trasmitter,也就是异步串行收发器。既然有异步串行收发器,那肯定也有同步串行收发器,学过STM32的同学应该知道,STM32除了有UART外,还有另外一个叫做USART的东西。USART的全称是Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步/异步串行收发器。相比UART多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线。一般USART是可以作为UART使用的,也就是不使用其同步的功能。
UART作为串口的一种,其工作原理也是将数据一位一位的进行传输,发送和接收各用一条线,因此通过UART接口与外界相连最少只需要三条线:TXD(发送)、RXD(接收)和GND(地线)。图21.1.1.1就是UART的通信格式:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第2张图片

图21.1.1.1 UART通信格式
图21.1.1.1中各位的含义如下:
空闲位:数据线在空闲状态的时候为逻辑“1”状态,也就是高电平,表示没有数据线空闲,没有数据传输。
起始位:当要传输数据的时候先传输一个逻辑“0”,也就是将数据线拉低,表示开始数据传输。
数据位:数据位就是实际要传输的数据,数据位数可选择5~8位,我们一般都是按照字节传输数据的,一个字节8位,因此数据位通常是8位的。低位在前,先传输,高位最后传输。
奇偶校验位:这是对数据中“1”的位数进行奇偶校验用的,可以不使用奇偶校验功能。
停止位:数据传输完成标志位,停止位的位数可以选择1位、1.5位或2位高电平,一般都选择1位停止位。
波特率:波特率就是UART数据传输的速率,也就是每秒传输的数据位数,一般选择9600、19200、115200等。
2、UART电平标准
UART一般的接口电平有TTL和RS-232,一般开发板上都有TXD和RXD这样的引脚,这些引脚低电平表示逻辑0,高电平表示逻辑1,这个就是TTL电平。RS-232采用差分线,-3-15V表示逻辑1,+3+15V表示逻辑0。一般图21.1.1.2中的接口就是TTL电平:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第3张图片
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第4张图片

图21.1.1.2 TTL电平接口
图21.1.1.2中的模块就是USB转TTL模块,TTL接口部分有VCC、GND、RXD、TXD、RTS和CTS。RTS和CTS基本用不到,使用的时候通过杜邦线和其他模块的TTL接口相连即可。
RS-232电平需要DB9接口,I.MX6U-ALPHA开发板上的COM3(UART3)口就是RS-232接口的,如图21.1.1.3所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第5张图片

图21.1.1.3 I.MX6U-ALPHA开发板RS-232接口
由于现在的电脑都没有DB9接口了,取而代之的是USB接口,所以就催生出了很多USB转串口TTL芯片,比如CH340、PL2303等。通过这些芯片就可以实现串口TTL转USB。I.MX6U-ALPHA开发板就使用CH340芯片来完成UART1和电脑之间的连接,只需要一条USB线即可,如图21.1.1.4所示。
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第6张图片

图21.1.1.4 I.MX6U-ALPHA开发板USB转TTL接口
21.1.2 I.MX6U UART简介
上一小节介绍了UART接口,本小节来具体看一下I.MX6U的UART接口,I.MX6U一共有8个UART,其主要特性如下:
①、兼容TIA/EIA-232F标准,速度最高可到5Mbit/S。
②、支持串行IR接口,兼容IrDA,最高可到115.2Kbit/s。
③、支持9位或者多节点模式(RS-485)。
④、1或2位停止位。
⑤、可编程的奇偶校验(奇校验和偶校验)。
⑥、自动波特率检测(最高支持115.2Kbit/S)。
I.MX6U的UART功能很多,但是我们本章就只用到其最基本的串口功能,关于UART其它功能的介绍请参考《I.MX6ULL参考手册》第3561页的“Chapter 55 Universal Asynchronous Receiver/Transmitter(UART)”章节。
UART的时钟源是由寄存器CCM_CSCDR1的UART_CLK_SEL(bit)位来选择的,当为0的时候UART的时钟源为pll3_80m(80MHz),如果为1的时候UART的时钟源为osc_clk(24M),一般选择pll3_80m作为UART的时钟源。寄存器CCM_CSCDR1的UART_CLK_PODF(bit5:0)位是UART的时钟分频值,可设置063,分别对应164分频,一般设置为1分频,因此最终进入UART的时钟为80MHz。
接下来看一下UART几个重要的寄存器,第一个就是UART的控制寄存器1,即UARTx_UCR1(x=1~8),此寄存器的结构如图21.1.2.1所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第7张图片

图21.1.2.1 寄存器UARTx_UCR1结构
寄存器UARTx_UCR1我们用到的重要位如下:
ADBR(bit14):自动波特率检测使能位,为0的时候关闭自动波特率检测,为1的时候使能自动波特率检测。
UARTEN(bit0):UART使能位,为0的时候关闭UART,为1的时候使能UART。
接下来看一下UART的控制寄存器2,即:UARTx_UCR2,此寄存器结构如图21.1.2.2所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第8张图片

图21.1.2.2 寄存器UARTx_UCR2结构
寄存器UARTx_UCR2用到的重要位如下:
IRTS(bit14):为0的时候使用RTS引脚功能,为1的时候忽略RTS引脚。
PREN(bit8):奇偶校验使能位,为0的时候关闭奇偶校验,为1的时候使能奇偶校验。
PROE(bit7):奇偶校验模式选择位,开启奇偶校验以后此位如果为0的话就使用偶校验,此位为1的话就使能奇校验。
STOP(bit6):停止位数量,为0的话1位停止位,为1的话2位停止位。
WS(bit5):数据位长度,为0的时候选择7位数据位,为1的时候选择8位数据位。
TXEN(bit2):发送使能位,为0的时候关闭UART的发送功能,为1的时候打开UART的发送功能。
RXEN(bit1):接收使能位,为0的时候关闭UART的接收功能,为1的时候打开UART的接收功能。
SRST(bit0):软件复位,为0的是时候软件复位UART,为1的时候表示复位完成。复位完成以后此位会自动置1,表示复位完成。此位只能写0,写1会被忽略掉。
接下来看一下UARTx_UCR3寄存器,此寄存器结构如图21.1.2.3所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第9张图片

图21.1.2.3 UARTx_UCR3寄存器结构体
本章实验就用到了寄存器UARTx_UCR3中的位RXDMUXSEL(bit2),这个位应该始终为1,这个在《I.MX6ULL参考手册》第3624页有说明。
接下来看一下寄存器UARTx_USR2,这个是UART的状态寄存器2,此寄存器结构如图21.1.2.4所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第10张图片

图21.1.2.4 寄存器UARTx_USR2结构
寄存器UARTx_USR2用到的重要位如下:
TXDC(bit3):发送完成标志位,为1的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向TxFIFO写入数据此位就会自动清零。
RDR(bit0):数据接收标志位,为1的时候表明至少接收到一个数据,从寄存器UARTx_URXD读取数据接收到的数据以后此位会自动清零。
接下来看一下寄存器UARTx_UFCR、UARTx_UBIR和UARTx_UBMR,寄存器UARTx_UFCR中我们要用到的是位RFDIV(bit9:7),用来设置参考时钟分频,设置如表21.1.2.1所示:
RFDIV(bit9:7) 分频值
000 6分频
001 5分频
010 4分频
011 3分频
100 2分频
101 1分频
110 7分频
111 保留
表21.1.2.1 RFDIV分频表
通过这三个寄存器可以设置UART的波特率,波特率的计算公式如下:

Ref Freq:经过分频以后进入UART的最终时钟频率。
UBMR:寄存器UARTx_UBMR中的值。
UBIR:寄存器UARTx_UBIR中的值。
通过UARTx_UFCR的RFDIV位、UARTx_UBMR和UARTx_UBIR这三者的配合即可得到我们想要的波特率。比如现在要设置UART波特率为115200,那么可以设置RFDIV为5(0b101),也就是1分频,因此Ref Freq=80MHz。设置UBIR=71,UBMR=3124,根据上面的公式可以得到:

=
最后来看一下寄存器UARTx_URXD和UARTx_UTXD,这两个寄存器分别为UART的接收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器UARTx_URXD即可获取到接收到的数据,如果要通过UART发送数据,直接将数据写入到寄存器UARTx_UTXD即可。
关于UART的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第3608页的55.15小节。本章我们使用I.MX6U的UART1来完成开发板与电脑串口调试助手之间串口通信, UART1的配置步骤如下:
1、设置UART1的时钟源
设置UART的时钟源为pll3_80m,设置寄存器CCM_CSCDR1的UART_CLK_SEL位为0即可。
2、初始化UART1
初始化UART1所使用IO,设置UART1的寄存器UART1_UCR1~UART1_UCR3,设置内容包括波特率,奇偶校验、停止位、数据位等等。
4、使能UART1
UART1初始化完成以后就可以使能UART1了,设置寄存器UART1_UCR1的位UARTEN为1。
5、编写UART1数据收发函数
编写两个函数用于UART1的数据收发操作。
21.2 硬件原理分析
本试验用到的资源如下:
①、一个LED灯:LED0。
②、串口1。
I.MX6U-ALPHA开发板串口1硬件原理图如图21.2.1所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第11张图片

图21.2.1 I.MX6U-ALPHA开发板串口1原理图
在做实验之前需要用USB串口线将串口1和电脑连接起来,并且还需要设置JP5跳线帽,将串口1的RXD、TXD两个引脚分别与P116、P117连接一起,如图21.2.2所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第12张图片

图21.2.2 串口1硬件连接设置图
硬件连接设置好以后就可以开始软件编写了,本章实验我们初始化好UART1,然后等待SecureCRT给开发板发送一个字节的数据,开发板接收到SecureCRT发送过来的数据以后在同通过串口1发送给SecureCRT。
21.3 实验程序编写
本实验对应的例程路径为:开发板光盘-> 1、裸机例程-> 13_uart。
本章实验在上一章例程的基础上完成,更改工程名字为“uart”,然后在bsp文件夹下创建名为“uart”的文件夹,然后在bsp/uart中新建bsp_uart.c和bsp_uart.h这两个文件。在bsp_uart.h中输入如下内容:

示例代码21.3.1 bsp_uart.h文件代码
1  #ifndef _BSP_UART_H
2  #define _BSP_UART_H
3  #include "imx6ul.h"
4  /***************************************************************
5  Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
6  文件名    : bsp_uart.h
7  作者      : 左忠凯
8  版本      : V1.0
9  描述      : 串口驱动文件头文件。
10 其他      : 无
11 论坛      : www.openedv.com
12 日志      : 初版V1.0 2019/1/15 左忠凯创建
13 ***************************************************************/
14 
15 /* 函数声明 */
16 void uart_init(void);
17 void uart_io_init(void);
18 void uart_disable(UART_Type *base);
19 void uart_enable(UART_Type *base);
20 void uart_softreset(UART_Type *base);
21 void uart_setbaudrate(UART_Type *base, 
unsigned int baudrate, 
unsigned int srcclock_hz);
22 void putc(unsigned char c);
23 void puts(char *str);
24 unsigned char getc(void);
25 void raise(int sig_nr);
26 
27 #endif
文件bsp_uart.h内容很简单,就是一些函数声明。继续在文件bsp_uart.c中输入如下所示内容:
示例代码21.3.2 bsp_uart.c文件代码
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名   : bsp_uart.c
作者     : 左忠凯
版本     : V1.0
描述     : 串口驱动文件。
其他     : 无
论坛     : www.openedv.com
日志     : 初版V1.0 2019/1/15 左忠凯创建
***************************************************************/
1   #include "bsp_uart.h"
2   
3   /*
4    * @description 	: 初始化串口1,波特率为115200
5    * @param       	: 无
6    * @return      	: 无
7    */
8   void uart_init(void)
9   {
10      /* 1、初始化串口IO 	*/
11      uart_io_init();
12  
13      /* 2、初始化UART1	*/
14      uart_disable(UART1);    		/* 先关闭UART1       	*/
15      uart_softreset(UART1);  		/* 软件复位UART1   		*/
16  
17      UART1->UCR1 = 0;        		/* 先清除UCR1寄存器 	*/
18      UART1->UCR1 &= ~(1<<14); 	/* 关闭自动波特率检测	*/
19      
20      /*
21       * 设置UART的UCR2寄存器,设置字长,停止位,校验模式,关闭硬件流控
22       * bit14: 1 忽略RTS引脚
23       * bit8:  0 关闭奇偶校验
24       * bit6:  0 1位停止位
25       * bit5:  1 8位数据位
26       * bit2:  1 打开发送
27       * bit1:  1 打开接收
28       */
29      UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);
30      UART1->UCR3 |= 1<<2; 			/* UCR3的bit2必须为1	*/
31        
32      /*
33       * 设置波特率
34       * 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)) 
35       * 如果要设置波特率为115200,那么可以使用如下参数:
36       * Ref Freq = 80M 也就是寄存器UFCR的bit9:7=101, 表示1分频
37       * UBMR = 3124
38       * UBIR =  71
39       * 因此波特率= 80000000/(16 * (3124+1)/(71+1))
40       *           = 80000000/(16 * 3125/72) 
41       *           = (80000000*72) / (16*3125) 
42       *           = 115200
43       */
44      UART1->UFCR = 5<<7;		 	/* ref freq等于ipg_clk/1=80Mhz */
45      UART1->UBIR = 71;
46      UART1->UBMR = 3124;
47      
48  #if 0
49      uart_setbaudrate(UART1, 115200, 80000000); /* 设置波特率 */
50  #endif
51  
52      uart_enable(UART1); /* 使能串口 */
53  }
54  
55  /*
56   * @description 	: 初始化串口1所使用的IO引脚
57   * @param       	: 无
58   * @return      	: 无
59   */
60  void uart_io_init(void)
61  {
62      /* 1、初始化串口IO
63       * UART1_RXD -> UART1_TX_DATA
64       * UART1_TXD -> UART1_RX_DATA
65       */
66      IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);
67      IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0); 
68      IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);
69      IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);
70  }
71  
72  /*
73   * @description       		: 波特率计算公式,
74   *                         	  可以用此函数计算出指定串口对应的UFCR,
75   *                         	  UBIR和UBMR这三个寄存器的值
76   * @param - base     		: 要计算的串口。
77   * @param - baudrate 		: 要使用的波特率。
78   * @param - srcclock_hz 	: 串口时钟源频率,单位Hz
79   * @return      			: 无
80   */
81  void uart_setbaudrate(UART_Type *base, 
unsigned int baudrate, 
unsigned int srcclock_hz)
82  {
83      uint32_t numerator = 0u;        
84      uint32_t denominator = 0U;      
85      uint32_t divisor = 0U;
86      uint32_t refFreqDiv = 0U;
87      uint32_t divider = 1U;
88      uint64_t baudDiff = 0U;
89      uint64_t tempNumerator = 0U;
90      uint32_t tempDenominator = 0u;
91  
92      /* get the approximately maximum divisor */
93      numerator = srcclock_hz;
94      denominator = baudrate << 4;
95      divisor = 1;
96  
97      while (denominator != 0)
98      {
99          divisor = denominator;
100         denominator = numerator % denominator;
101         numerator = divisor;
102     }
103 
104     numerator = srcclock_hz / divisor;
105     denominator = (baudrate << 4) / divisor;
106 
107     /* numerator ranges from 1 ~ 7 * 64k */
108     /* denominator ranges from 1 ~ 64k */
109     if ((numerator > (UART_UBIR_INC_MASK * 7)) || (denominator > 
UART_UBIR_INC_MASK))
110     {
111         uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
112         uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
113         uint32_t max = m > n ? m : n;
114         numerator /= max;
115         denominator /= max;
116         if (0 == numerator)
117         {
118             numerator = 1;
119         }
120         if (0 == denominator)
121         {
122             denominator = 1;
123         }
124     }
125     divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;
126 
127     switch (divider)
128     {
129         case 1:
130             refFreqDiv = 0x05;
131             break;
132         case 2:
133             refFreqDiv = 0x04;
134             break;
135         case 3:
136             refFreqDiv = 0x03;
137             break;
138         case 4:
139             refFreqDiv = 0x02;
140             break;
141         case 5:
142             refFreqDiv = 0x01;
143             break;
144         case 6:
145             refFreqDiv = 0x00;
146             break;
147         case 7:
148             refFreqDiv = 0x06;
149             break;
150         default:
151             refFreqDiv = 0x05;
152             break;
153     }
154     /* Compare the difference between baudRate_Bps and calculated 
155      * baud rate. Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
156      * baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / 
divider)/ denominator).
157      */
158     tempNumerator = srcclock_hz;
159     tempDenominator = (numerator << 4);
160     divisor = 1;
161     /* get the approximately maximum divisor */
162     while (tempDenominator != 0)
163     {
164         divisor = tempDenominator;
165         tempDenominator = tempNumerator % tempDenominator;
166         tempNumerator = divisor;
167     }
168     tempNumerator = srcclock_hz / divisor;
169     tempDenominator = (numerator << 4) / divisor;
170     baudDiff = (tempNumerator * denominator) / tempDenominator;
171     baudDiff = (baudDiff >= baudrate) ? (baudDiff - baudrate) : 
(baudrate - baudDiff);
172 
173     if (baudDiff < (baudrate / 100) * 3)
174     {
175         base->UFCR &= ~UART_UFCR_RFDIV_MASK;
176         base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
177         base->UBIR = UART_UBIR_INC(denominator - 1); 
178         base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
179     }
180 }
181 
182 /*
183  * @description  	: 关闭指定的UART
184  * @param – base 	: 要关闭的UART
185  * @return        	: 无
186  */
187 void uart_disable(UART_Type *base)
188 {
189     base->UCR1 &= ~(1<<0);  
190 }
191 
192 /*
193  * @description 	: 打开指定的UART
194  * @param – base	: 要打开的UART
195  * @return      	: 无
196  */
197 void uart_enable(UART_Type *base)
198 {
199     base->UCR1 |= (1<<0);   
200 }
201 
202 /*
203  * @description 	: 复位指定的UART
204  * @param – base	: 要复位的UART
205  * @return      	: 无
206  */
207 void uart_softreset(UART_Type *base)
208 {
209     base->UCR2 &= ~(1<<0);  				/* 复位UART  	*/
210     while((base->UCR2 & 0x1) == 0); 	/* 等待复位完成	*/
211 }
212 
213 /*
214  * @description 	: 发送一个字符
215  * @param - c   	: 要发送的字符
216  * @return      	: 无
217  */
218 void putc(unsigned char c)
219 {
220     while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 	*/
221     UART1->UTXD = c & 0XFF;                 	 /* 发送数据 			*/
222 }
223 
224 /*
225  * @description 	: 发送一个字符串
226  * @param - str 	: 要发送的字符串
227  * @return      	: 无
228  */
229 void puts(char *str)
230 {
231     char *p = str;
232 
233     while(*p)
234         putc(*p++);
235 }
236 
237 /*
238  * @description 	: 接收一个字符
239  * @param       	: 无
240  * @return      	: 接收到的字符
241  */
242 unsigned char getc(void)
243 {
244     while((UART1->USR2 & 0x1) == 0);	/* 等待接收完成 		*/
245     return UART1->URXD;             		/* 返回接收到的数据 	*/
246 }
247 
248 /*
249  * @description 	: 防止编译器报错
250  * @param       	: 无
251  * @return      	: 无
252  */
253 void raise(int sig_nr) 
254 {
255 
256 }

文件bsp_uart.c中共有10个函数,我们依次来看一下这些函数都是做什么的,第一个函数是uart_init,这个函数是UART1初始化函数,用于初始化UART1相关的IO、并且设置UART1的波特率、字长、停止位和校验模式等,初始化完成以后就使能UART1。第二个函数是uart_io_init,用于初始化UART1所使用的IO。第三个函数是uart_setbaudrate,这个函数是从NXP官方的SDK包里面移植过来的,用于设置波特率。我们只需将要设置的波特率告诉此函数,此函数就会使用逐次逼近方式来计算出寄存器UART1_UFCR的FRDIV位、寄存器UART1_UBIR和寄存器UART1_UBMR这三个的值。第四和第五这两个函数为uart_disable和uart_enable,分别是使能和关闭UART1。第6个函数是uart_softreset,用于软件复位指定的UART。第七个函数是putc,用于通过UART1发送一个字节的数据。第八个函数是puts,用于通过UART1发送一串数据。第九个函数是getc,用于通过UART1获取一个字节的数据,最后一个函数是raise,这是一个空函数,防止编译器报错。
最后在main.c中输入如下所示内容:

示例代码21.3.3 main.c文件代码
/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名   : main.c
作者     : 左忠凯
版本     : V1.0
描述     : I.MX6U开发板裸机实验13 串口实验
其他     : 本实验我们学习如何使用I.MX6的串口,实现串口收发数据,了解
           I.MX6的串口工作原理。
论坛     : www.openedv.com
日志     : 初版V1.0 2019/1/15 左忠凯创建
**************************************************************/
1  #include "bsp_clk.h"
2  #include "bsp_delay.h"
3  #include "bsp_led.h"
4  #include "bsp_beep.h"
5  #include "bsp_key.h"
6  #include "bsp_int.h"
7  #include "bsp_uart.h"
8  
9  /*
10  * @description	: main函数
11  * @param        	: 无
12  * @return       	: 无
13  */
14 int main(void)
15 {
16  	unsigned char a=0;
17  	unsigned char state = OFF;
18 
19  	int_init();              	/* 初始化中断(一定要最先调用!)	*/
20  	imx6u_clkinit();        	/* 初始化系统时钟            	*/
21  	delay_init();            	/* 初始化延时          			*/
22  	clk_enable();            	/* 使能所有的时钟            	*/
23  	led_init();              	/* 初始化led             		*/
24  	beep_init();             	/* 初始化beep            		*/
25  	uart_init();              	/* 初始化串口,波特率115200 	*/
26 
27  	while(1)                
28  	{   
29      	puts("请输入1个字符:");
30      	a=getc();
31      	putc(a);    			/* 回显功能 */ 
32      	puts("\r\n");
33 
34      	/* 显示输入的字符 */ 
35      	puts("您输入的字符为:");
36      	putc(a);
37      	puts("\r\n\r\n");
38      
39      	state = !state;
40      	led_switch(LED0,state);
41  	}
42  	return 0;
43 }
第5行调用函数uart_init初始化UART1,最终在while循环里面获取串口接收到的数据,并且将获取到的数据通过串口打印出来。

21.4 编译下载验证
21.4.1 编写Makefile和链接脚本
在Makefile文件中输入如下内容:

示例代码21.4.1 Makefile文件代码
1  CROSS_COMPILE	?= arm-linux-gnueabihf-
2  TARGET          	?= uart
3  
4  CC              	:= $(CROSS_COMPILE)gcc
5  LD              	:= $(CROSS_COMPILE)ld
6  OBJCOPY      	:= $(CROSS_COMPILE)objcopy
7  OBJDUMP      	:= $(CROSS_COMPILE)objdump
8  
9  LIBPATH        	:= -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01- x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4
10 
11 
12 INCDIRS      	:=	imx6ul \
13                 		bsp/clk \
14                 		bsp/led \
15                 		bsp/delay  \
16                 		bsp/beep \
17                 		bsp/gpio \
18                 		bsp/key \
19                		bsp/exit \
20                 		bsp/int \
21                	 	bsp/epittimer \
22                 		bsp/keyfilter \
23                 		bsp/uart 
24                             
25 SRCDIRS        	:=	project \
26                 		bsp/clk \
27                 		bsp/led \
28                		bsp/delay \
29                 		bsp/beep \
30                 		bsp/gpio \
31                 		bsp/key \
32                 		bsp/exit \
33                 		bsp/int \
34                 		bsp/epittimer \
35                 		bsp/keyfilter \
36                 		bsp/uart 
37                 
38                 
39 INCLUDE        	:= $(patsubst %, -I %, $(INCDIRS))
40 
41 SFILES         	:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
42 CFILES         	:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
43 
44 SFILENDIR      	:= $(notdir  $(SFILES))
45 CFILENDIR      	:= $(notdir  $(CFILES))
46 
47 SOBJS          	:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
48 COBJS         	:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
49 OBJS         	:= $(SOBJS) $(COBJS)
50 
51 VPATH         	:= $(SRCDIRS)
52 
53 .PHONY: clean
54  
55 $(TARGET).bin : $(OBJS)
56  	$(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH)
57  	$(OBJCOPY) -O binary -S $(TARGET).elf $@
58  	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis
59 
60 $(SOBJS) : obj/%.o : %.S
61  	$(CC) -Wall -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<
62 
63 $(COBJS) : obj/%.o : %.c
64  	$(CC) -Wall -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<
65  
66 clean:
67  rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

上述的Makefile文件内容和上一章实验的区别不大。将TARGET为uart,在INCDIRS和SRCDIRS中加入“bsp/uart”。但是,相比上一章中的Makefile文件,本章实验的Makefile有两处重要的改变:
①、本章Makefile文件在链接的时候加入了数学库, 因为在bsp_uart.c中有个函数uart_setbaudrate,在此函数中使用到了除法运算,因此在链接的时候需要将编译器的数学库也链接进来。第9行的变量LIBPATH就是数学库的目录,在第56行链接的时候使用了变量LIBPATH。
在后面的学习中,我们常常要用到一些第三方库,那么在连接程序的时候就需要指定这些第三方库所在的目录,Makefile在链接的时候使用选项“-L”来指定库所在的目录,比如“示例代码21.4.1”中第9行的变量LIBPATH就是指定了我们所使用的编译器库所在的目录。
②、在第61行和64行中,加入了选项“-fno-builtin”,否则编译的时候提示“putc”、“puts”这两个函数与内建函数冲突,错误信息如下所示:
warning: conflicting types for built-in function ‘putc’
warning: conflicting types for built-in function ‘puts’
在编译的时候加入选项“-fno-builtin”表示不使用内建函数,这样我们就可以自己实现putc和puts这样的函数了。
链接脚本保持不变。
21.4.2 编译下载
使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的uart.bin文件下载到SD卡中,命令如下:
chmod 777 imxdownload //给予imxdownload可执行权限,一次即可
./imxdownload uart.bin /dev/sdd //烧写到SD卡中,不能烧写到/dev/sda或sda1设备里面!
烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板。打开SourceCRT,点击File->Quick Connect…,打开快速连接设置界面,设置好相应的串口参数,比如在我的电脑上是COM8,设置如图21.4.2.1所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第13张图片

图21.4.2.1 SecureCRT串口设置
设置好以后就点击“Connect”就可以了,连接成功以后SecureCRT收到来自开发板的数据,但是SecureCRT显示可能会是乱码,如图21.4.2.2所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第14张图片

图21.4.2.2 SecureCRT显示乱码
这是因为有些设置还没做,点击Options->Session Options…,打开会话设置窗口,按照图21.4.2.3所示设置:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第15张图片

图21.4.2.3 会话设置
设置好以后点击“OK”按钮就可以了,清屏,然后重新复位一次开发板,此时SecureCRT显示就正常了,如图21.4.2.4所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第16张图片

图21.4.2.4 显示正常
根据提示输入一个字符,这个输入的字符就会通过串口发送给开发板,开发板接收到字符以后就会通过串口提示你接收到的字符是什么,如图21.4.2.5所示:
【正点原子Linux连载】第二十一章 UART串口通信实验-摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0_第17张图片

图21.4.2.5 实验效果
至此,I.MX6U的串口1就工作起来了,以后我们就可以通过串口来调试程序。但是本章只实现了串口最基本的收发功能,如果我们要想使用格式化输出话就不行了,比如最常用的printf函数,下一章就讲解如何移植printf函数。

你可能感兴趣的:(LINUX)