• I.MX6ull UART


     一 简介

    UART 全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。I.MX6U 自带的 UART 外设就是 串口的一种,UART 全称是 Universal Asynchronous Receiver/Trasmitter,也就是异步串行收发器。 既然有异步串行收发器,那肯定也有同步串行收发器,除 了有 UART 外,还有 另外一 个叫做 USART 的东西 。USART 的 全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步/异步串行收发器。相比 UART 多了 一个同步的功能,在硬件上体现出来的就是多了一条时钟线。一般 USART 是可以作为 UART

    6U UART      

    1 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)”章节。

    二 结构原理图

    2.1  时钟源

    参考IMX6ULL芯片手册《Chapter 18: Clock Controller Module (CCM)》,
    根据IMX6ULL的时钟树,设置CSCDR1寄存器就可以给UART提供总时钟,如下图:

    下图是CSCDR1(CCM Serial Clock Divider Register 1)的位说明,对于UART,我们选择时钟源位80M:

    CCM_SDCDR1->UART_CLK_SEL  = 0,  1分频  ~3F 

    见I.MX6ull 时钟系统

     2.2  内部结构

    UART 一般的接口电平有 TTL (TTL是 Time To Live的缩写,是生存时间值的意思)  RS-232,一般开发板上都有 TXD 和 RXD 这样的引脚, 这些引脚低电平表示逻辑 0,高电平表示逻辑 1,这个就是 TTL 电平。RS-232 采用差分线,-3~- 15V 表示逻辑 1,+3~+15V 表示逻辑 0。一般图 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 所示:

     

    由于现在的电脑都没有 DB9 接口了,取而代之的是 USB 接口,所以就催生出了很多 USB

    转串口 TTL 芯片,比如 CH340、PL2303 等。通过这些芯片就可以实现串口 TTL 转 USB。I.MX6UALPHA 开发板就使用 CH340 芯片来完成 UART1 和电脑之间的连接,只需要一条USB 线即可, 对于 V2.4 版以前的底板,如图 21.1.1.4 所示。

    2.3 工作原理

            UART 作为串口的一种,其工作原理也是将数据一位一位的进行传输,发送和接收各用一 条线,因此通过 UART 接口与外界相连最少只需要三条线:TXD(发送)、RXD(接收)和 GND(地 线)。图 21.1.1.1 就是 UART 的通信格式:

    2.3.1 各部分意义如下:

    空闲位:数据线在空闲状态的时候为逻辑“1”状态,也就是高电平,表示没有数据线空闲, 没有数据传输。

    起始位:当要传输数据的时候先传输一个逻辑“0”,也就是将数据线拉低,表示开始数据 传输。

    数据位:数据位就是实际要传输的数据,数据位数可选择 5~8 位,我们一般都是按照字节 传输数据的,一个字节 8 位,因此数据位通常是 8 位的。低位在前,先传输,高位最后传输。

    奇偶校验位:这是对数据中“1”的位数进行奇偶校验用的,可以不使用奇偶校验功能。

    停止位:数据传输完成标志位,停止位的位数可以选择 1 位、1.5 位或 2 位高电平,一般都 选择 1 位停止位。

    波特率:波特率就是 UART 数据传输的速率,也就是每秒传输的数据位数,一般选择 9600、

    19200、115200 等。

              

    2.3.2 串口模块内部结构

    串口模块共有两个时钟输入 Peripheral Clock 和 Module Clock。

    • Peripheral Clock

    外部时钟。这个时钟主要用于读、写接收和发送的数据,例如读接收FIFO、写发送FIFO。 这个时钟与波特率设置无关,如果没有特殊需求我们将这个时钟保持默认即可,在初始代码中并没有特意设置这个时钟。

    • Module Clock

    模块时钟,它既可用于接收、发送数据也用于设置波特率,这个时钟决定了串口最高支持的波特率。Module Clock 时钟来自根时钟 UART_CLK_ROOT。

    通过搜索可以找到UART1时钟使能的控制位。CCM_CCGR5 时钟树默认频率80M

    2.4 重要寄存器

    UARTx_UCR1  UARTx_UCR2   UARTx_UCR3  ,UARTx_USR2, CCM_CCGR5(main 函数一开始使能了所以外设时钟) 

    2.4.1 控制寄存器 UARTx_UCRx

    1 UARTx_UCR1 

    • ADBR(bit14):自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使 能自动波特率检测。
    • UARTEN(bit0):UART 使能位,为 0 的时候关闭 UART,为 1 的时候使能 UART。

    2 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 会被忽略掉。

    3  UARTx_UCR3

    本章实验就用到了寄存器 UARTx_UCR3 中的位 RXDMUXSEL(bit2),这个位应该始终为 1, 这个在《I.MX6ULL 参考手册》第 3624 页有说明。

     红外输入混合模式

    2.4.2、状态寄存器 UARTx_USR2

    • TXDC(bit3):发送完成标志位,为 1 的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也 就是发送完成,向 TxFIFO 写入数据此位就会自动清零。 RDR(bit0):数据接收标志位,为 1 的时候表明至少接收到一个数据,从寄存器
    • UARTx_URXD 读取数据接收到的数据以后此位会自动清零。

    2.4.3 分频寄存器  UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR

      UART 的波特率 

    通过这三个寄存器可以设置 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,根据上面的 公式可以得到:

    实际使用时直接调用对应方法即可不必过于关心推导过程

    2.4.4 UARTx_URXD , UARTx_UTXD 

    最后来看一下寄存器 UARTx_URXD 和 UARTx_UTXD,这两个寄存器分别为 UART 的接 收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器

    UARTx_URXD 即可获取到接收到的数据,如果要通过 UART 发送数据,直接将数据写入到寄 存器 UARTx_UTXD 即可。

    三 使用实验

    通过 UART1读写并显示输出

    3.1 配置步骤

    • UART1 的配置步骤如下:

    • 1、设置 UART1 的时钟源

      设置 UART 的时钟源为 pll3_80m,设置寄存器 CCM_CSCDR1 的 UART_CLK_SEL 位为 0

      即可。

    • 2、初始化 UART1

      初始化 UART1 所使用 IO,设置 UART1 的寄存器 UART1_UCR1~UART1_UCR

    • 3,设置内 容包括波特率,奇偶校验、停止位、数据位等等。

    • 4、使能 UART1

      UART1 初始化完成以后就可以使能 UART1 了,设置寄存器 UART1_UCR1 的位 UARTEN

      为 1。

      5、编写 UART1 数据收发函数

      编写两个函数用于 UART1 的数据收发操作。

    3.2 代码 

    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_led.c
    7. 作者 : 左忠凯
    8. 版本 : V1.0
    9. 描述 : 串口驱动文件头文件。
    10. 其他 : 无
    11. 论坛 : www.wtmembed.com
    12. 日志 : 初版V1.0 2019/1/15 左忠凯创建
    13. ***************************************************************/
    14. /* 函数声明 */
    15. void uart_init(void);
    16. void uart_io_init(void);
    17. void uart_disable(UART_Type *base);
    18. void uart_enable(UART_Type *base);
    19. void uart_softreset(UART_Type *base);
    20. void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz);
    21. void putc(unsigned char c);
    22. void puts(char *str);
    23. unsigned char getc(void);
    24. void raise(int sig_nr);
    25. #endif

    bsp_uart.c

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

    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. * @description : main函数
    10. * @param : 无
    11. * @return : 无
    12. */
    13. int main(void)
    14. {
    15. unsigned char a=0;
    16. unsigned char state = OFF;
    17. int_init(); /* 初始化中断(一定要最先调用!) */
    18. imx6u_clkinit(); /* 初始化系统时钟 */
    19. delay_init(); /* 初始化延时 */
    20. clk_enable(); /* 使能所有的时钟 */
    21. led_init(); /* 初始化led */
    22. beep_init(); /* 初始化beep */
    23. uart_init(); /* 初始化串口,波特率115200 */
    24. while(1)
    25. {
    26. puts("请输入1个字符:");
    27. a=getc();
    28. putc(a); //回显功能
    29. puts("\r\n");
    30. //显示输入的字符
    31. puts("您输入的字符为:");
    32. putc(a);
    33. puts("\r\n\r\n");
    34. state = !state;
    35. led_switch(LED0,state);
    36. }
    37. return 0;
    38. }

  • 相关阅读:
    正则表达式的神奇世界:表达、匹配和提取
    Libcurl 学习与使用
    移动端开发环境 - Mac
    【Java】中Maven依赖详解
    git clone报错SSL connect error
    29.Java中的文件操作【20220804】
    软件系统建模&架构风格-架构论文(三十八)
    【FPGA教程案例11】基于vivado核的除法器设计与实现
    系列十、ReentrantReadWriteLock
    如何从任何苹果、Windows或安卓设备访问iCloud照片
  • 原文地址:https://blog.csdn.net/TyearLin/article/details/131151868