嵌入式裸机开发-UART 串口通信

UART

  • I.MX6U 串口
    • I.MX6U 串口简介
      • UART 简介
      • I.MX6U UART 简介
    • 硬件原理分析
    • 驱动编写
    • 总结
  • 串口格式化函数
    • 串口格式化函数简介
    • 驱动编写

I.MX6U 串口

I.MX6U 串口简介

UART 简介


UART 通信格式
串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。
UART 全称是 Universal Asynchronous Receiver/Trasmitter,全双工异步串行收发器。通过 UART 接口与外界相连最少只需要三条线: TXD(发送)、 RXD(接收)和 GND(地线)
1帧数据=1位起始位+8位数据位+(1位奇偶校验位)+1 ~ 2位停止位

嵌入式裸机开发-UART 串口通信_第1张图片
空闲位:数据线在空闲状态的时候为逻辑“1”状态,也就是高电平,表示没有数据线空闲,没有数据传输。
起始位:当要传输数据的时候先传输一个逻辑“0”,也就是将数据线拉低,表示开始数据传输。
数据位: 数据位就是实际要传输的数据,数据位数可选择 5~8 位,我们一般都是按照字节传输数据的,一个字节 8 位,因此数据位通常是 8 位的。低位在前,高位在后。
奇偶校验位: 这是对数据中“1”的位数进行奇偶校验用的,可以不使用奇偶校验功能。
停止位:数据传输完成标志位,停止位的位数可以选择 1 位、 1.5 位或 2 位高电平,一般都选择 1 位停止位。
波特率:波特率就是 UART 数据传输的速率,也就是每秒传输的数据位数,一般选择 9600、19200、 115200 等。


UART 电平标准
UART 一般的接口电平有 TTLRS-232。开发板上一般都是TTL电平。

TTL 电平:高电平5V表示逻辑 1,低电平0V表示逻辑 0。
RS-232电平:-3V ~ -15V表示逻辑1,+3V ~ +15V表示逻辑0。

RS422电平和RS485都采用差分传输,利用了两根线A、B的电压差,AB表示逻辑。
差分传输抗干扰能力强,更适合远距离传输。

以上TTL,RS232,RS422,RS485这些都是电平标准,都能使用UART串口协议,TTL和RS232使用2根数据线+1根地线,RS485使用4根数据线,RS422使用2根数据线且只有它不是全双工。RS-232 电平过去常使用 DB9 接口,现在CH340进行电平转换可使用USB接口


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 其它功能的介绍请参考《I.MX6ULL 参考手册》第 3561 页的
Chapter 55 UniversalAsynchronousReceiver/Transmitter(UART)”章节。


UART同样需要选择时钟源分频值,使用寄存器CCM_CSCDR1。
嵌入式裸机开发-UART 串口通信_第2张图片
UART_CLK_SEL(6 bit)时钟源选择
当为 0 的时候 UART 的时钟源为 pll3_80m(80MHz),
当为 1 的时候 UART 的时钟源为 osc_clk(24M),
一般选择 pll3_80m 作为 UART 的时钟源。
UART_CLK_PODF(bit5:0)时钟分频值
可设置 0 ~ 63,分别对应 1 ~ 64 分频,一般设置为 1 分频。


UART 的控制寄存器 1,即UARTx_UCR1(x=1~8)。
嵌入式裸机开发-UART 串口通信_第3张图片
寄存器 UARTx_UCR1 我们用到的重要位如下:
ADBR(bit14):自动波特率检测使能位,
为 0 的时候关闭自动波特率检测,
为 1 的时候使能自动波特率检测。
UARTEN(bit0): UART 使能位,
为 0 的时候关闭 UART,
为 1 的时候使能 UART。


UART 的控制寄存器 2,即: UARTx_UCR2(x=1~8)。
嵌入式裸机开发-UART 串口通信_第4张图片
寄存器 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 的时候表示复位完成。
此位只能写 0复位,复位完成以后此位会自动置 1, 表示复位完成,写 1 会被忽略掉。


UARTx_UCR3 寄存器(x=1~8)。
嵌入式裸机开发-UART 串口通信_第5张图片
RXDMUXSEL(bit2):RXD 复位选择,这个位应该始终为 1。


寄存器 UARTx_USR2,这个是 UART 的状态寄存器 2。
嵌入式裸机开发-UART 串口通信_第6张图片
寄存器 UARTx_USR2 用到的重要位如下:
TXDC(bit3):发送完成标志位,
为 1 的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向 TxFIFO 写入数据此位就会自动清零。
RDR(bit0):数据接收标志位,
为 1 的时候表明至少接收到一个数据,从寄存器UARTx_URXD 读取数据接收到的数据以后此为会自动清零。


波特率设置用到了 UARTx_UFCR 的 RFDIV 位(bit9:7)、 UARTx_UBMR 和 UARTx_UBIR 三个寄存器。
嵌入式裸机开发-UART 串口通信_第7张图片
没想到波特率计算还有一个分频(目前两个分频,一个时钟分频,一个波特率分频)。
在这里插入图片描述
Ref Freq:经过分频以后进入 UART 的最终时钟频率。
UBMR:寄存器 UARTx_UBMR 中的值。
UBIR:寄存器 UARTx_UBIR 中的值。
比如现在要设置 UART 波特率为 115200,那么可以设置 RFDIV 为5(0b101),也就是 1 分频,因此 Ref Freq=80MHz。设置 UBIR=71,UBMR=3124

这个计算公式也搞不通为啥,跟AT89C51的波特率计算都不一样,大概是别人规定好的吧,我越来越觉得嵌入式就是个工具了,[/微笑][/微笑][/微笑]还是多学学算法吧。


寄存器 UARTx_URXD 和 UARTx_UTXD,这两个寄存器的低八位(bit7:bit0)分别为 UART 的接收和发送数据寄存器。
读取寄存器UARTx_URXD 即可获取到接收到的数据,
直接将数据写入到寄存器 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 的数据收发操作。

硬件原理分析

嵌入式裸机开发-UART 串口通信_第8张图片
在做实验之前需要用 USB 串口线将串口 1 和电脑连接起来,并且还需要设置 JP5 跳线帽将串口 1 的 RXD、 TXD 两个引脚分别于P116、 P117 连接一起。

驱动编写

uart.h

#ifndef _BSP_UART_H
#define _BSP_UART_H

#include "imx6ul.h"

void uart_init(void);
void putc(unsigned char c);
void puts(char *str);
unsigned char getc(void);


#endif

uart.c

#include "bsp_uart.h"

/*初始化串口 1,波特率为 115200 */
void uart_init(void)
{
     
    //1、初始化串口IO GPIO1_IO16和GPIO1_IO17 复用 保持功能
    IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0);    
    IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);
	
	IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0); //做接收端,需要额外配置输入寄存器
	IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);  

    //2、初始化UART1
    UART1->UCR1 &= ~(1<<0); //先关闭 UART1
    UART1->UCR2 &= ~(1<<0);  //软件复位 UART1
    while((UART1->UCR2 & 0x01)==0);
    UART1->UCR1 = 0;            //先清除 UCR1 寄存器
    UART1->UCR1 &= ~(1<<14);    //关闭自动波特率检测 我们自己设置波特率

    UART1->UCR2 |= (1<<14);     //忽略 RTS 引脚
    UART1->UCR2 &= ~(1<<8);     //关闭奇偶校验
    UART1->UCR2 &= ~(1<<6);     //1 位停止位
    UART1->UCR2 |= (1<<5);      //8 位数据位
    UART1->UCR2 |= (1<<2);      //打开发送
    UART1->UCR2 |= (1<<1);      //打开接收
	
    UART1->UCR3 |= (1<<2);      //UCR3 的 bit2 必须为 1

    //3、设置波特率 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1))
    //Ref Freq = 80M 也就是寄存器 UFCR 的 bit9:7=101, 表示 1 分频  UBMR = 3124   UBIR = 71
    //先配置UBIR再配置UBMR
	UART1->UFCR = (5<<7);
	UART1->UBIR = 71;
    UART1->UBMR = 3124;
    


    UART1->UCR1 |= (1<<0);    //使能串口

}

/*发送一个字符 8bit*/
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 & 0x01)==0); //等待接收完成
    return UART1->URXD;
}


Makefile

$(CC) -Wall -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<

在编译时候加入了选项“-fno-builtin”,否则编译的时候提示“putc”、“puts”这两个函数与内建函数冲突

总结

UART的驱动编写就是先设置时钟源和分频系数,初始化IO口,先关闭UART,配置相关寄存器,设置波特率,最后使能UART。发送/接受字节之前都要等待发送/接受完成标志,然后再向UTXD/URXD中写入/读取数据。
SecureCRT这个软件可以实现Windows和板子之间的串口通信、SSH通信。
在下载验证时遇到的问题:串口收发数据出现乱码,且键盘输入字符几乎失灵,感觉波特率有问题。
解决问题:使用排除法和标准程序对比,发现波特率设置时候要先配置UBIR,再配置UBMR!
先配置UBIR,再配置UBMR!
先配置UBIR,再配置UBMR!
嵌入式裸机开发-UART 串口通信_第9张图片
查阅开发手册的寄存器使用也是如此,检查了好久,在崩溃边缘疯狂试探。

串口格式化函数

串口格式化函数简介

格式化函数说的是 printf、 sprintf 和 scanf 这样的函数,分为格式化输入格式化输出两类函数。

驱动编写

实现了最基本的字节收发,本章我们就通过移植网上别人已经做好的文件来实现格式化函数。

“站在巨人的肩膀,直接用别人造好的轮子!”
从 uboot 里面移植过来stdio文件夹,文件夹内包含:
嵌入式裸机开发-UART 串口通信_第10张图片
这里要注意一点, stdio 中并没有实现完全版的格式化函数,比如 printf 函数并不支持浮点数,但是基本够我们使用了,这样我们就能像C语言编程屏幕交互一样进行PC和arm通信,通过直接调用stdio内写好的库函数,用法类似
uboot 源码里面其实还有很多其他功能文件。

//直接包含头文件
#include "stdio.h"
//用法和C语言相似
printf("输入两个整数,使用空格隔开:");
scanf("%d %d", &a, &b); /* 输入两个整数 */
printf("\r\n 数据%d + %d = %d\r\n\r\n", a, b, a+b);/* 输出和 */

你可能感兴趣的:(雨露均沾的知识)