串口全称叫做串行接口,通常也叫做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的数据收发操作。
-
- #include "bsp_uart.h"
-
- /*
- * @description : 初始化串口1,波特率为115200
- * @param : 无
- * @return : 无
- */
- void uart_init(void)
- {
- /* 1、初始化串口IO */
- uart_io_init();
-
- /* 2、初始化UART1 */
- uart_disable(UART1); /* 先关闭UART1 */
- uart_softreset(UART1); /* 软件复位UART1 */
-
- UART1->
- UCR1 = 0; /* 先清除UCR1寄存器 */
-
- /*
- * 设置UART的UCR1寄存器,关闭自动波特率
- * bit14: 0 关闭自动波特率检测,我们自己设置波特率
- */
- UART1->UCR1 &= ~(1<<14);
-
- /*
- * 设置UART的UCR2寄存器,设置内容包括字长,停止位,校验模式,关闭RTS硬件流控
- * bit14: 1 忽略RTS引脚
- * bit8: 0 关闭奇偶校验
- * bit6: 0 1位停止位
- * bit5: 1 8位数据位
- * bit2: 1 打开发送
- * bit1: 1 打开接收
- */
- UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);
-
- /*
- * UART1的UCR3寄存器
- * bit2: 1 必须设置为1!参考IMX6ULL参考手册3624页
- */
- UART1->UCR3 |= 1<<2;
-
- /*
- * 设置波特率
- * 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1))
- * 如果要设置波特率为115200,那么可以使用如下参数:
- * Ref Freq = 80M 也就是寄存器UFCR的bit9:7=101, 表示1分频
- * UBMR = 3124
- * UBIR = 71
- * 因此波特率= 80000000/(16 * (3124+1)/(71+1))=80000000/(16 * 3125/72) = (80000000*72) / (16*3125) = 115200
- */
- UART1->UFCR = 5<<7; //ref freq等于ipg_clk/1=80Mhz
- UART1->UBIR = 71;
- UART1->UBMR = 3124;
-
- #if 0
- uart_setbaudrate(UART1, 115200, 80000000); /* 设置波特率 */
- #endif
-
- /* 使能串口 */
- uart_enable(UART1);
- }
-
- /*
- * @description : 初始化串口1所使用的IO引脚
- * @param : 无
- * @return : 无
- */
- void uart_io_init(void)
- {
- /* 1、初始化IO复用
- * UART1_RXD -> UART1_TX_DATA
- * UART1_TXD -> UART1_RX_DATA
- */
- IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0); /* 复用为UART1_TX */
- IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0); /* 复用为UART1_RX */
-
- /* 2、配置UART1_TX_DATA、UART1_RX_DATA的IO属性
- *bit 16:0 HYS关闭
- *bit [15:14]: 00 默认100K下拉
- *bit [13]: 0 keeper功能
- *bit [12]: 1 pull/keeper使能
- *bit [11]: 0 关闭开路输出
- *bit [7:6]: 10 速度100Mhz
- *bit [5:3]: 110 驱动能力R0/6
- *bit [0]: 0 低转换率
- */
- IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);
- IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);
- }
-
- /*
- * @description : 波特率计算公式,
- * 可以用此函数计算出指定串口对应的UFCR,
- * UBIR和UBMR这三个寄存器的值
- * @param - base : 要计算的串口。
- * @param - baudrate : 要使用的波特率。
- * @param - srcclock_hz :串口时钟源频率,单位Hz
- * @return : 无
- */
- void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz)
- {
- uint32_t numerator = 0u; //分子
- uint32_t denominator = 0U; //分母
- uint32_t divisor = 0U;
- uint32_t refFreqDiv = 0U;
- uint32_t divider = 1U;
- uint64_t baudDiff = 0U;
- uint64_t tempNumerator = 0U;
- uint32_t tempDenominator = 0u;
-
- /* get the approximately maximum divisor */
- numerator = srcclock_hz;
- denominator = baudrate << 4;
- divisor = 1;
-
- while (denominator != 0)
- {
- divisor = denominator;
- denominator = numerator % denominator;
- numerator = divisor;
- }
-
- numerator = srcclock_hz / divisor;
- denominator = (baudrate << 4) / divisor;
-
- /* numerator ranges from 1 ~ 7 * 64k */
- /* denominator ranges from 1 ~ 64k */
- if ((numerator > (UART_UBIR_INC_MASK * 7)) || (denominator > UART_UBIR_INC_MASK))
- {
- uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
- uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
- uint32_t max = m > n ? m : n;
- numerator /= max;
- denominator /= max;
- if (0 == numerator)
- {
- numerator = 1;
- }
- if (0 == denominator)
- {
- denominator = 1;
- }
- }
- divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;
-
- switch (divider)
- {
- case 1:
- refFreqDiv = 0x05;
- break;
- case 2:
- refFreqDiv = 0x04;
- break;
- case 3:
- refFreqDiv = 0x03;
- break;
- case 4:
- refFreqDiv = 0x02;
- break;
- case 5:
- refFreqDiv = 0x01;
- break;
- case 6:
- refFreqDiv = 0x00;
- break;
- case 7:
- refFreqDiv = 0x06;
- break;
- default:
- refFreqDiv = 0x05;
- break;
- }
- /* Compare the difference between baudRate_Bps and calculated baud rate.
- * Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
- * baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / divider)/ denominator).
- */
- tempNumerator = srcclock_hz;
- tempDenominator = (numerator << 4);
- divisor = 1;
- /* get the approximately maximum divisor */
- while (tempDenominator != 0)
- {
- divisor = tempDenominator;
- tempDenominator = tempNumerator % tempDenominator;
- tempNumerator = divisor;
- }
- tempNumerator = srcclock_hz / divisor;
- tempDenominator = (numerator << 4) / divisor;
- baudDiff = (tempNumerator * denominator) / tempDenominator;
- baudDiff = (baudDiff >= baudrate) ? (baudDiff - baudrate) : (baudrate - baudDiff);
-
- if (baudDiff < (baudrate / 100) * 3)
- {
- base->UFCR &= ~UART_UFCR_RFDIV_MASK;
- base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
- base->UBIR = UART_UBIR_INC(denominator - 1); //要先写UBIR寄存器,然后在写UBMR寄存器,3592页
- base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
- }
- }
-
- /*
- * @description : 关闭指定的UART
- * @param - base: 要关闭的UART
- * @return : 无
- */
- void uart_disable(UART_Type *base)
- {
- base->UCR1 &= ~(1<<0);
- }
-
- /*
- * @description : 打开指定的UART
- * @param - base: 要打开的UART
- * @return : 无
- */
- void uart_enable(UART_Type *base)
- {
- base->UCR1 |= (1<<0);
- }
-
- /*
- * @description : 复位指定的UART
- * @param - base: 要复位的UART
- * @return : 无
- */
- void uart_softreset(UART_Type *base)
- {
- base->UCR2 &= ~(1<<0); /* UCR2的bit0为0,复位UART */
- while((base->UCR2 & 0x1) == 0); /* 等待复位完成 */
- }
-
- /*
- * @description : 发送一个字符
- * @param - c : 要发送的字符
- * @return : 无
- */
- void putc(unsigned char c)
- {
- while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */
- UART1->UTXD = c & 0XFF; /* 发送数据 */
- }
-
- /*
- * @description : 发送一个字符串
- * @param - str : 要发送的字符串
- * @return : 无
- */
- void puts(char *str)
- {
- char *p = str;
-
- while(*p)
- putc(*p++);
- }
-
- /*
- * @description : 接收一个字符
- * @param : 无
- * @return : 接收到的字符
- */
- unsigned char getc(void)
- {
- while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */
- return UART1->URXD; /* 返回接收到的数据 */
- }
-
- /*
- * @description : 防止编译器报错
- * @param : 无
- * @return : 无
- */
- void raise(int sig_nr)
- {
-
- }
-
-
结果: