1. 背景知识
1.1 UART通讯格式
??串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。UART为异步串行收发器。 ??数据位:数据位就是实际要传输的数据,数据位数可选择 5~8 位,我们一般都是按照字节传输数据的,一个字节 8 位,因此数据位通常是 8 位的。低位在前,先传输,高位最后传输。 ??具体通讯格式如下图所示:
1.2 UART电平标准
??UART 一般的接口电平有 TTL 和 RS-232,一般开发板上都有 TXD 和 RXD 这样的引脚,这些引脚低电平表示逻辑 0,高电平表示逻辑 1,这个就是 TTL 电平。 RS-232 采用差分线, -3~-15V 表示逻辑 1, +3~+15V 表示逻辑 0。
1.3 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) ??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。 ??通过设置寄存器UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR,可以得到我们想要的波特率。寄存器UARTx_UFCR 中我们要用到的是位 RFDIV(bit9:7),用来设置参考时钟分频,而UBMR、UBIR直接使用UARTx_UBIR 和 UARTx_UBMR寄存器中的值,计算公式如下: ??最后来看一下寄存器 UARTx_URXD 和 UARTx_UTXD,这两个寄存器分别为 UART的接收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器UARTx_URXD 即可获取到接收到的数据,如果要通过 UART 发送数据,直接将数据写入到寄存器 UARTx_UTXD 即可。 ??综上所述,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 的数据收发操作。
2. 代码编写
??我们在UART的c文件中总共编写10个函数, uart_init用于初始化 UART1 相关的 IO、并且设置 UART1的波特率、字长、停止位和校验模式等,最后使能UART1。uart_io_init,用于初始化 UART1 所使用的 IO。uart_setbaudrate是从NXP官方的SDK包中移植过来的,用于设置波特率。uart_disable 和uart_enable,分别是使能和关闭 UART1。第 6 个函数是 uart_softreset,用于软件复位指定的 UART。第七个函数是putc,用于通过UART1发送一个字节的数据。第八个函数是puts,用于通过UART1发送一串数据。第九个函数是 getc,用于通过 UART1 获取一个字节的数据,最后一个函数是raise,这是一个空函数,防止编译器报错。
void uart_init(void)
{
uart_io_init();
uart_disable(UART1);
uart_softreset(UART1);
UART1->
UCR1 = 0;
UART1->UCR1 &= ~(1<<14);
UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);
UART1->UCR3 |= 1<<2;
UART1->UFCR = 5<<7;
UART1->UBIR = 71;
UART1->UBMR = 3124;
#if 0
uart_setbaudrate(UART1, 115200, 80000000);
#endif
uart_enable(UART1);
}
void uart_io_init(void)
{
IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0);
IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0);
IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);
IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);
}
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;
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;
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;
}
tempNumerator = srcclock_hz;
tempDenominator = (numerator << 4);
divisor = 1;
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);
base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
}
}
void uart_disable(UART_Type *base)
{
base->UCR1 &= ~(1<<0);
}
void uart_enable(UART_Type *base)
{
base->UCR1 |= (1<<0);
}
void uart_softreset(UART_Type *base)
{
base->UCR2 &= ~(1<<0);
while((base->UCR2 & 0x1) == 0);
}
void putc(unsigned char c)
{
while(((UART1->USR2 >> 3) &0X01) == 0);
UART1->UTXD = c & 0XFF;
}
void puts(char *str)
{
char *p = str;
while(*p)
putc(*p++);
}
unsigned char getc(void)
{
while((UART1->USR2 & 0x1) == 0);
return UART1->URXD;
}
void raise(int sig_nr)
{
}
??我们只需要在main.c函数中调用uart_init,而后在while循环中接受PC机传来的字符并将其回发,在PC端的串口调试助手中显示出来。
int main(void)
{
unsigned char a=0;
unsigned char state = OFF;
int_init();
imx6u_clkinit();
delay_init();
clk_enable();
led_init();
beep_init();
uart_init();
while(1)
{
puts("请输入1个字符:");
a=getc();
putc(a);
puts("\r\n");
puts("您输入的字符为:");
putc(a);
puts("\r\n\r\n");
state = !state;
led_switch(LED0,state);
}
return 0;
}
??最终,我们在串口调试程序SecureCRT中发现I.MX6U的串口1能够正常工作起来。
|