• 串口实验言言


    串口简介

    串口全称叫做串行接口,通常也叫做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(地线)。图1就是UART的通信格式:

    2、UART电平标准

    UART一般的接口电平有TTL和RS-232,一般开发板上都有TXD和RXD这样的引脚,这些引脚低电平表示逻辑0,高电平表示逻辑1,这个就是TTL电平。RS-232采用差分线,-3~-15V表示逻辑1,+3~+15V表示逻辑0。一般图2中的接口就是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.MX6U-ALPHA开发板就使用CH340芯片来完成UART1和电脑之间的连接,只需要一条USB线即可,

    3.I.MXU-ALPHA的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),此寄存器的结构如图:

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

    UARTEN(bit0):UART使能位,为0的时候关闭UART,为1的时候使能UART。

    接下来看一下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会被忽略掉。

    接下来看一下UARTx_UCR3寄存器,此寄存器结构如图:

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

    TXDC(bit3):发送完成标志位,为1的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向TxFIFO写入数据此位就会自动清零。

    RDR(bit0):数据接收标志位,为1的时候表明至少接收到一个数据,从寄存器UARTx_URXD读取数据接收到的数据以后此位会自动清零。

    通过这UARTx_UFCR、UARTx_UBIR和UARTx_UBMR三个寄存器可以设置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即可。

    4.实验步骤:

    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的数据收发操作。

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

    结果:

     

  • 相关阅读:
    模型工厂,让AI算法触手可达 | StartDT Tech Lab 05
    闭关之 C++ 并发编程笔记(一):线程与锁
    现代循环神经网络 - 编码器-解码器架构
    python怎么实现Excel表的折叠功能
    go语言包管理和变量保护
    渗透测试(PenTest)基础指南
    <Python的语句>——《Python》
    RocketMQ(16)——Name Server可配置参数介绍
    如何安装Nacos服务器?
    Android逆向fiddler抓包工具——理解HTTP协议
  • 原文地址:https://blog.csdn.net/qq_66545503/article/details/126217357