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地理位置查询,以及文…...
bpmn+vue 中文文档
1.初始化项目 <script> import BpmnModeler from bpmn-js/lib/Modeler import { xmlStr } from /mock/xmlStr export default {mounted () {this.init()},methods: {init () {// 获取到属性ref为“canvas”的dom节点const canvas this.$refs.canvas// 建模const custom…...
React Router v5 和 v6 中,路由对象声明方式有什么区别?
一、在React Router 6.x开始,路由对象的声明需要引用RouteObject。 import { RouteObject } from react-router-dom;const routes: RouteObject[] [{path: /,element: <Home />},{path: /about,element: <About />},// ... ];二、一些老项目使用的是R…...
【全开源】知识库文档系统(ThinkPHP+FastAdmin)
📚知识库文档系统:解锁知识的无限可能 一款基于ThinkPHPFastAdmin开发的知识库文档系统,可用于企业工作流程的文档管理,结构化记录沉淀高价值信息,形成完整的知识体系,能够轻松提升知识的流转和传播效率&a…...
Python赋能自然语言处理,解锁通往AI的钥匙
NLTK(Natural Language Toolkit)是一个用于 Python 的自然语言处理库,提供了丰富的工具和资源,帮助处理、分析和理解人类语言数据.它广泛应用于学术研究、教育和商业应用中. 安装 #首先要安装 NLTK:pip install nltk安装完成后,还需要下载…...
Ktor库的高级用法:代理服务器与JSON处理
在现代网络编程中,Ktor是一个高性能且易于使用的框架,它提供了对异步编程、WebSockets、HTTP客户端和服务器等特性的原生支持。Ktor是使用Kotlin语言编写的,充分利用了Kotlin的协程特性来简化异步编程。本文将深入探讨Ktor库的高级用法&#…...
VS2017配置OpenCV4.5.1
VS2017配置OpenCV 一、下载OpenCV二、配置OpenCV的电脑环境变量三、配置visual Studio添加路径复制文件到C盘 四、如何使用注意运行时选择Debug x64 五、报错:VSOpencv出现:xxx处有未经处理的异常: Microsoft C 异常: cv::Exception,位于内存…...
phpstudy配置的站点不能访问了
无法打开站点 打开网站的时候出现如下 没有人为主动去更改配置项,今天就不能正常访问了 检查了一遍配置,发现并无大碍,那就重新配置一遍看看 配置phpstudy 1、新建网站 2、选择项目入口文件夹 3、配置伪静态 4. 确认保存 在我的电脑 C:\…...
Java Web学习笔记2——Web开发介绍
什么是Web? Web:全球广域网,也称为万维网(WWW World Wide Web),能够通过浏览器访问的网站。 1)淘宝、京东、唯品会等电商系统; 2)CRM、OA、ERP企业管理系统࿱…...
从零开始实现自己的串口调试助手(3) - 显示底部收发,优化串口打开/关闭
注意: 1. 我们要实现自发自收,要将tx,rx连起来 2.发送的 不能是中文符号,因为这可能导致,读取到的是英文符号 --> 导致接收到的size 和发送的size 大小不一致 3.注意同时定义两个槽函数的时候两个槽函数都会被调用,…...
更改Web网站设计——css和css框架
虽然使用HTML可以定义文章的结构,但是其中不包含设计相关的信息。此时CSS就派上用场,可以用它对HTML文章指定设计样式。由于可以决定Web网页的外观风格,因此,它有时也被称为格式表。 如果使用CSS设置背景色,文…...
株洲专业网站建设品牌/软文写作模板
2019独角兽企业重金招聘Python工程师标准>>> 其实一直想写一篇文章名字都想好了,叫做“程序员该不该理财?”。后来想了想,该不该这个就不用想了,必须要理财!那么市面上那么多理财的方式对于我们屌丝的程序员…...
宝塔wordpress教程/快推达seo
为什么80%的码农都做不了架构师?>>> #coding:utf-8直方图 import cv2 import numpy as np import matplotlib.pyplot as plt 计算RGB图像每个通道的直方图def cal_histgram_rgb(image):image_rgb cv2.cvtColor(image,cv2.COLOR_BGR2RGB)channel_r,chan…...
东莞手机网站建设入门/搜索引擎营销推广
抽象类(abstract class): abstract 修饰符用于表示所修饰的类是不完整的,并且它只能用作基类。抽象类与非抽象类在以下方面是不同的: 抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误。虽然一些变量和值在编译时的类型可以是…...
新闻网站品牌栏目建设情况/网页设计作品集
Win10 OS安装(配置)MySQL 5.7(解压版) 下载及解压 下载地址:https://dev.mysql.com/downloads/mysql/5.7.html#downloads文件名:mysql-5.7.27-win32.zipzip是解压版,msi是安装版,本教…...
wordpress侧边栏滚动/爱站工具包官网
EasyNVR网页无插件直播平台经过多次的修正和改良,已经成为一套成熟的流媒体视频云服务平台,能够满足安防、智慧城市、教育等不同场景的应用需求。 测试EasyNVR拉流,在确认输入源问题的时候,一般会使用VLC进行拉流对比测试源是否存…...
网络公司注册费用/seo 网站优化推广排名教程
四种类型 CISCO防火墙技术汇总高手进阶 PIX防火墙校园网配置实例转载于:https://blog.51cto.com/xaosky/14015...