I.MX6ULL UART 串口通信实验
系列文章目录
I.MX6ULL UART 串口通信实验
I.MX6ULL UART 串口通信实验
- 系列文章目录
- 一、前言
- 二、I.MX6U 串口简介
- 2.1 UART 简介
- 2.2 I.MX6U UART 简介
- 三、硬件原理分析
- 四、实验程序编写
- 五、编译下载验证
- 5.1编写 Makefile 和链接脚本
- 5.2 编译下载
一、前言
不管是单片机开发还是嵌入式 Linux 开发,串口都是最常用到的外设。可以通过串口将开发板与电脑相连,然后在电脑上通过串口调试助手来调试程序。还有很多的模块,比如蓝牙、GPS、GPRS 等都使用的串口来与主控进行通信的,在嵌入式 Linux 中一般使用串口作为控制台,所以掌握串口是必备的技能。本节学习如何驱动 I.MX6U 上的串口,并使用串口和电脑进行通信。
二、I.MX6U 串口简介
2.1 UART 简介
1、UART 通信格式
串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。
I.MX6U 自带的 UART 外设就是串口的一种,UART 全称是 UniversalAsynchronous Receiver/Trasmitter,也就是异步串行收发器。既然有异步串行收发器,那肯定也有同步串行收发器,学过 STM32 的同学应该知道,STM32除了有 UART 外,还有 另外一 个叫做 USART 的东西 。USART 的 全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步/异步串行收发器。相比 UART 多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线。一般 USART 是可以作为 UART使用的,也就是不使用其同步的功能。
UART 作为串口的一种,其工作原理也是将数据一位一位的进行传输,发送和接收各用一条线,因此通过 UART 接口与外界相连最少只需要三条线:TXD(发送)、RXD(接收)和 GND(地线)。图中就是 UART 的通信格式:

图中各位的含义如下:
空闲位: 数据线在空闲状态的时候为逻辑“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。一般图中的接口就是 TTL 电平:

图中的模块就是 USB 转 TTL 模块,TTL 接口部分有 VCC、GND、RXD、TXD、RTS 和 CTS。RTS 和 CTS 基本用不到,使用的时候通过杜邦线和其他模块的 TTL 接口相连即可。
RS-232 电平需要 DB9 接口,I.MX6U-ALPHA 开发板上的 COM3(UART3)口就是 RS-232 接口的,如图所示:

由于现在的电脑都没有 DB9 接口了,取而代之的是 USB 接口,所以就催生出了很多 USB转串口 TTL 芯片,比如 CH340、PL2303 等。通过这些芯片就可以实现串口 TTL 转 USB。I.MX6UALPHA 开发板就使用 CH340 芯片来完成 UART1 和电脑之间的连接,只需要一条USB 线即可,如图所示

2.2 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 的时钟分频值,可设置 0~63,分别对应 1~64 分频,一般设置为 1 分频,因此最终进入 UART 的时钟为 80MHz。
接下来看一下 UART 几个重要的寄存器,第一个就是 UART 的控制寄存器 1,即UARTx_UCR1(x=1~8),此寄存器的结构如图所示:

寄存器 UARTx_UCR1 我们用到的重要位如下:
ADBR(bit14):自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使能自动波特率检测。
UARTEN(bit0):UART 使能位,为 0 的时候关闭 UART,为 1 的时候使能 UART。
接下来看一下 UART 的控制寄存器 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 寄存器,此寄存器结构如图所示:
本章实验就用到了寄存器 UARTx_UCR3 中的位 RXDMUXSEL(bit2),这个位应该始终为 1,这个在《I.MX6ULL 参考手册》第 3624 页有说明。

接下来看一下寄存器 UARTx_USR2,这个是 UART 的状态寄存器 2,此寄存器结构如图所示:

寄存器 UARTx_USR2 用到的重要位如下:
TXDC(bit3):发送完成标志位,为 1 的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向 TxFIFO 写入数据此位就会自动清零。
RDR(bit0):数据接收标志位,为 1 的时候表明至少接收到一个数据,从寄存器UARTx_URXD 读取数据接收到的数据以后此位会自动清零。
接下来看一下寄存器 UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR ,寄存器UARTx_UFCR 中我们要用到的是位 RFDIV(bit9:7),用来设置参考时钟分频,设置如表示:

通过这三个寄存器可以设置 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,设置内容包括波特率,奇偶校验、停止位、数据位等等。
3、使能 UART1
UART1 初始化完成以后就可以使能 UART1 了,设置寄存器 UART1_UCR1 的位 UARTEN为 1。
4、编写 UART1 数据收发函数
编写两个函数用于 UART1 的数据收发操作。
三、硬件原理分析
本试验用到的资源如下:
①、一个 LED 灯:LED0。
②、串口 1。
LED硬件原理图参考

在做实验之前需要用 USB 串口线将串口 1 和电脑连接起来,并且还需要设置 JP5 跳线帽,将串口 1 的 RXD、TXD 两个引脚分别与 P116、P117 连接一起,如图所示:

硬件连接设置好以后就可以开始软件编写了,本节实验我们初始化好 UART1,然后等待SecureCRT 给开发板发送一个字节的数据,开发板接收到 SecureCRT 发送过来的数据以后在同通过串口 1 发送给 SecureCRT。
四、实验程序编写
本节实验在该文章的基础上完成,更改工程名字为“uart”,然后在 bsp 文件夹下创建名为“uart”的文件夹,然后在 bsp/uart 中新建 bsp_uart.c 和 bsp_uart.h 这两个文件。在 bsp_uart.h中输入如下内容:
1 #ifndef _BSP_UART_H
2 #define _BSP_UART_H
3 #include "imx6ul.h"
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 中输入如下所示内容:
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 中输入如下所示内容:
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 循环里面获取串口接收到的数据,并且将获取到的数据通过串口打印出来。
五、编译下载验证
5.1编写 Makefile 和链接脚本
在 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”来指定库所在的目录,比如“示例代码中第 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 这样的函数了。
链接脚本保持不变。
5.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(插上你的开发板到电脑上,你电脑噔噔的声音响查看你多了那个端口就是COM几)
设置如图所示:

设置好以后就点击“Connect”就可以了,连接成功以后 SecureCRT 收到来自开发板的数据,但是 SecureCRT 显示可能会是乱码,如图所示:

这是因为有些设置还没做,点击 Options->Session Options…,打开会话设置窗口,按照图所示设置:

设置好以后点击“OK”按钮就可以了,清屏,然后重新复位一次开发板,此时 SecureCRT显示就正常了,如图所示:

根据提示输入一个字符,这个输入的字符就会通过串口发送给开发板,开发板接收到字符
以后就会通过串口提示你接收到的字符是什么,如图所示:
至此,I.MX6U 的串口 1 就工作起来了,以后我们就可以通过串口来调试程序。
END

相关文章:
I.MX6ULL UART 串口通信实验
系列文章目录 I.MX6ULL UART 串口通信实验 I.MX6ULL UART 串口通信实验 系列文章目录一、前言二、I.MX6U 串口简介2.1 UART 简介2.2 I.MX6U UART 简介 三、硬件原理分析四、实验程序编写五、编译下载验证5.1编写 Makefile 和链接脚本5.2 编译下载 一、前言 不管是单片机开发还…...
systemctlm-cosim-demo项目分析
概述 systemctlm-cosim-demo项目是Xilinx的systemc库的demo工程。 环境安装 qemu安装 cd xilinx_proj/Downloads git clone https://github.com/Xilinx/qemu.git cd qemu git checkout 74d70f8008# Configure and build # zynq7000 # ./configure --target-list"arm-s…...
SQL学习小记(四)Navicat中连接Oracle数据库的详细步骤
五分钟解决Oracle连接问题:DPI-1047: Cannot locate a 64-bit Oracle Client library: “The specified module could not be SQL学习小记(四)Navicat中连接Oracle 1. 错误信息2. 解决过程2.1.版本查询2.2. 官网下载2.3. 设置Navicat的oci环…...
mysql聚簇索引
1.聚簇索引是物理索引,数据在表里是按顺序存储的,物理上是连续的,一般选主键id作为聚簇索引,且一张表里只能有一个聚簇索引。 2.只有InnoDB支持聚簇索引。 3.非聚簇索引是逻辑索引,将数据的某个字段抽取出来组成独立的…...
【云原生】Kubernetes----PersistentVolume(PV)与PersistentVolumeClaim(PVC)详解
目录 引言 一、存储卷 (一)存储卷定义 (二)存储卷的作用 1.数据持久化 2.数据共享 3.解耦 4.灵活性 (三)存储卷的分类 1.emptyDir存储卷 1.1 定义 1.2 特点 1.3 示例 2.hostPath存储卷 2.1 …...
Adobe Premiere 专业视频编辑软件资源下载安装!pr 2024最新版软件分享
Adobe Premiere,作为一款非线性视频编辑软件,它打破了传统线性编辑的限制,为用户提供了更加灵活和自由的创作空间。 在电影制作领域,Adobe Premiere的出色表现赢得了众多导演和剪辑师的青睐。其强大的编辑功能使得影片的剪辑过程更…...
c++------类和对象(下)包含了this指针、构造函数、析构函数、拷贝构造等
文章目录 前言一、this指针1.1、this指针的引出1.2、 this指针的特性 二、类的默认的六个构造函数2.1、构造函数简述2.2构造函数 三、析构函数3.1、析构函数引出3.2、特点: 四、拷贝构造4.1、引入4.2、特征:4.3、默认拷贝构造函数 总结 前言 在本节中&a…...
git版本控制工具常用命令
一、本地仓库管理 push 向远程推送代码 pulll 拉取代码 二、远程仓库管理 三、分支操作 本地主分支master 远程主分支main head指向当前分支 查看:git branch 创建分支: git branch 名字 切换分支:git checkout 名字 合并分支:git…...
展示广告多模态召回模型:混合模态专家模型
✍🏻 本文作者:俊广、卓立、凌潼、青萤 1. 背景 随着在搜索、推荐、广告技术上多年的迭代积累,业界逐步形成了召回(匹配)、粗排、精排这一多阶段的系统架构。其中,召回作为链路的最前端,决定了业…...
好的一些网安资源
镜像:https://msdn.itellyou.cn/ 编程学习{ 菜鸟教程:https://www.runoob.com/ w3school:https://www.w3school.com.cn/ https://www.dotcpp.com/ http://zh.cppreference.com/ https://beginnersbook.com/ https://www.ai8py.com/ }…...
Linux chmod 命令
Linux chmod 命令 在 Linux 操作系统中,chmod 命令是非常重要的。它可以用于修改文件和目录的访问权限,以及控制用户对系统资源的访问。在这篇博客中,我们将深入探讨 chmod 命令的使用方法,以及如何使用它来管理文件和目录的访问…...
SDL教程(二)——Qt+SDL播放器
前言 这篇文章主要是使用SDL来打开视频,显示视频。后续会再继续使用SDL来结合FFmpeg。来能够直接使用网上的demo进行学习。 正文 一、环境 Qt 5.15.2 MSVC2019 64bit Win11 二、Qt搭建SDL Qt搭建,我觉得相比用VS2019来说,更为方便&…...
Java面试题:解决Redis缓存击穿问题
缓存击穿 当一个key过期时,需要对这个key进行数据重建 在重建的时间内如果有大量的并发请求进入,就会绕过缓存进入数据库,会瞬间击垮DB 重建时间可能因为数据是多个表的混合结果需要分头统计而延长,从而更容易出现缓存击穿问题 缓存击穿的解决方案 添加互斥锁 先查询缓存…...
Redis的哨兵模式
什么是哨兵模式 Redis的哨兵模式( Sentinel mode )是⼀个⾼可⽤解决⽅案,当运⾏多个 Redis 实例并且需要⾃动故障转移时,哨兵模式⾮常有⽤。 在⼀个典型的哨兵模式下,⾄少需要3 个哨兵实例来避免 “ 脑裂 ” ÿ…...
Hadoop伪分布式安装教程
Hadoop伪分布式安装教程 一、安装背景1.1 软件列表1.2 系统软件列表 二、安装Hadoop2.1 安装 Java 环境2.1.1 前期准备2.1.2 文件传输2.1.3 解压文件2.1.4 配置 jdk 的环境变量2.1.5 输入 java、javac、java -version 命令检验 jdk 是否安装成功 2.2 Hadoop 下载地址[hadoop](h…...
Day43 代码随想录打卡|二叉树篇---左叶子之和
题目(leecode T404): 给定二叉树的根节点 root ,返回所有左叶子之和。 方法: 迭代法:计算所有的左叶子节点,那我们就必然要找到所有的左叶子节点。那么怎么找呢?如何针对cur->l…...
微信小程序动画
微信小程序动画属性:提升用户体验的利器 引言 随着移动互联网技术的快速发展,微信小程序已经成为开发者和用户的热门选择。其轻便、快捷、即用即走的特点使得它在各种场景中都有广泛的应用。而动画作为一种重要的视觉元素,在增强用户体验方…...
js, ellipsis属性, 超出宽度自动省略... , 并且显示2行
overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; text-overflow:ellipsis;...
ucharts自定义添加tooltip悬浮框显示项内容且换行
欢迎点击领取 -《前端开发面试题进阶秘籍》:前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的🪜 一、需求描述分析 1、小程序上实现图表的绘制,多条线路,不同颜色,悬浮框对应内容不同数据进行处理 2…...
Mongo 地理位置查询:海量密集点转换成聚合信息
通俗来说:将地图上的海量密集点通过网格分割的方式实现聚合; 需求:用mongo实现设备地理位置聚合查询 :多边形,矩形查询; 背景:上万设备数据量 目的:分享Mongo地理位置查询,以及文…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
