• 14. UART串口通信


    1. UART

    1.1 UART 通信格式

    串口通常也叫 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线就可以实现双向通信,一条用于发送,一条用于接口。UART 是异步串行收发器,与外界相连最少需要三条线:TXD发送、RXD接收和 GND地线。在这里插入图片描述
    空闲位: 数据线在空闲状态的时候为逻辑1状态,也就是高电平,表示没有数据线空闲,没有数据传输。
    起始位: 当要传输数据的时候先传输一个逻辑0,也就是将数据线拉低,表示开始数据传输
    数据位: 数据位就是实际要传输的数据,可选择 5 ~ 8 位,一般都是按照字节传输数据的,一个字节 8 位,因此数据位通常是 8 位的,低位在前,先传输,高位最后传输。
    奇偶检验位: 这是对数据中 1 的位数进行奇偶检验用的,可以不使用该功能
    停止位: 数据传输完成标志位,停止位的位数可以选择 1、1.5 或 2 位高电平,一般都选择 1 位停止位
    波特率: 就是 UART 数据传输的速率,也就是每秒传输的数据位数,一般选择 9600、19200、115200 等

    1.2 UART 电平标准

    UART 一般的接口电平有 TTL 和 RS-232。TTL 电平低电平表示逻辑 0,高电平表示逻辑 1;RS-232 采用差分线,-3 ~ -15 表示逻辑 1,3 ~ 15 表示逻辑 0.
    在这里插入图片描述
    上图所示就是 TTL 电平接口,下图是 RS-232电平接口
    在这里插入图片描述
    但是现在笔记本几乎没有这种接口,就有了usb转TTL芯片

    1.3 I.MX6U UART 简介

    一共有 8 个UART,主要特性如下:

    1. 兼容 TIA/EIA-232F标准,速度最高可达 5Mbit/s
    2. 支持串行 IR 接口,兼容 IrDA,最高可达115.2Kbit/s
    3. 支持 9 位或者多节点模式
    4. 1 或 2 位停止位
    5. 可编程的奇偶检验
    6. 自动波特率检查,最高支持 115.2 Kbit/s
      这里只用到最基本的串口功能。UART 的时钟源是由寄存器 CCM_CSCDR1 的 UART_CLK_SEL 位来选择的,当为 0 的时候 UART 的时钟源为 pll3_80m(80MHz),如果为 1 的时候 UART 的时钟源为 osc_clk(24M),一般选择 pll3_80m。寄存器 CCM_CSCDR1 的 UART_CLK_PODF 位是 UART 的时钟分频值,可设置 0 ~ 63,分别对应 1 ~ 64 分频,一般设置 1 分频,因此最终进入 UART 的时钟源是 80 MHz

    1.3.1 控制寄存器1 UARTx_UCR1(x=1~8)

    在这里插入图片描述
    ADBR(bit14): 自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使能波特率检测
    UARTEN(bit0): UART 使能位,为 0 的时候关闭 UART,为 1 的时候使能

    1.3.2 控制寄存器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 关闭发送功能,1 打开发送功能
    RXEN(bit1): 接收使能位,0 关闭接收功能,1 打开接收功能
    SRST(bit0): 软件复位,0 的时候软件复位 UART,为 1 的时候表示复位完成,复位完成后此位会自动置 1,表示复位完成。此位只能写0,写1会被忽略

    1.3.3 控制寄存器3 UARTx_UCR3

    在这里插入图片描述
    这里只用到了 RXDMUXSEL(bit2),这个位应该始终为1

    1.3.4 状态寄存器2 UARTx_USR2

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

    1.3.4 UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR

    UARTx_UFCR 用到的位是 RFDIV(bit9:7),用来设置参考时钟分频
    在这里插入图片描述
    通过这三个寄存器可设置波特率,公式如下:
    在这里插入图片描述
    Ref Freq:经过分频以后进入 UART 的最终时钟频率
    UBMR:寄存器UBMR中的值
    UBIR:寄存器UBIR中的值
    如果要设置115200,那么设置 RFDIV 为 1 分频,Ref Freq=80MHz,UBIR=71,UBMX=3124

    1.3.5 UARTx_URXD 和 UARTx_UTXD

    这两个是接收和发送数据寄存器,低八位为接收到的和要发送的数据。

    1.4 代码实例

    bsp_uart.h

    #pragma once
    #include "imx6ul.h"
    
    void uart_init();
    void uart_io_init();
    void uart_disable(UART_Type *base);
    void uart_enable(UART_Type *base);
    void uart_softreset(UART_Type *base);
    void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz);
    void putc(unsigned char c);
    void puts(char *str);
    unsigned char getc();
    void raise(int sig_nr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    bsp_uart.c

    #include "bsp_uart.h"
    
    // 初始化串口1,波特率为115200
    void uart_init()
    {
    	// 初始化串口 IO
    	uart_io_init();
    
    	// 初始化 UART1
    	uart_disable(UART1);		// 先关闭UART1
    	uart_softreset(UART1);		// 软件复位UART1
    	UART1->UCR1=0;				// 先清除 UCR1 寄存器
    	UART1->UCR1 &= ~(1<<14);	// 关闭自动波特率检测
    
    	// 设置 UCR2 寄存器,忽略RTS引脚,关闭奇偶检验,设置1位停止位,8位数据位,打开发送和接收
    	UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);
    
    	UART1->UCR3 |= 1<<2;
    
    	// 设置波特率
    	UART1->UFCR = 5<<7;
    	UART1->UBIR = 71;
    	UART->UBMR = 3124;
    
    	// 使能串口
    	uart_enable(UART1);
    }
    
    // 初始化串口1所使用的引脚
    void uart_io_init()
    {
    	IOMUXC_SetPinMUX(IOMUXC_UART1_TX_DATA_UART1_TX, 0);	// 复用为UART1_TXD
    	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);
    }
    
    // 关闭指定的UART
    void uart_disable(UART_Type *base)
    {
    	base->UCR1 &= ~(1<<0);
    }	
    
    // 打开指定的UART
    void uart_enable(UART_Type *base)
    {
    	base->UCR1 |= (1<<0);	
    }
    
    // 复位指定的UART
    void uart_softreset(UART_Type *base)
    {
    	base->UCR2 &= ~(1<<0);				// 复位
    	while((base->UCR2 & 0x1) == 0);		// 等待复位完成,复位完成后此位自动置1
    }
    
    // 设置波特率,由官方 SDK 包移植过来
    void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz);
    
    // 发送一个字符
    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()
    {
    	while((UART1->USR2 & 0x1)==0);	// 等待接收完成
    	return UART1->URXD;
    }
    
    // 防止编译器报错
    void raise(int sig_nr)
    {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    main.c

    #include "bsp_clk.h"
    #include "bsp_delay.h"
    #include "bsp_led.h"
    #include "bsp_beep.h"
    #include "bsp_key.h"
    #include "bsp_int.h"
    #include "bsp_uart.h"
    
    int main()
    {
    	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("请输入一个字符:");
    		a=getc();
    		putc(a);	// 数据回显,就是将输入的数据显示出来,如果没有这一行,效果就像linux系统中输入密码什么都不显示一样
    		puts("\r\n");
    			
    		puts("输入的字符为:");
    		putc(a);
    		puts("\r\n\r\n");
    
    		state = !state;
    		led_switch(LED0, state);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    1.5 编译下载

    在通用makefile文件中,将 TARGET 修改为 uart,在 INCDIRS 和 SRCDIRS 中加入 bsp/uart。在设置波特率函数中使用到了除法运算,链接的时候需要将编译器的数学库也链接进来,并且使用 -L来指定库所在的目录。

    LIBPATH := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4
    
    • 1

    -fno-builtin表示不使用内建函数,也就可以自己实现该函数,否则会发生冲突

    $(SOBJS) : obj/%.o : %.S
    	$(CC) -Wall -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<
    $(COBJS) : obj/%.o : %.c
    	$(CC) -Wall -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<
    
    • 1
    • 2
    • 3
    • 4

    2. 串口格式化函数

    2.1 代码实例

    其余函数基本不变,多添加一个stdio.h头文件

    int main()
    {
    	int a, b;
    	unsigned char state = OFF;
    	int_init();
    	imx6u_clkinit();
    	delay_init();
    	clk_enable();
    	led_init();
    	beep_init();
    	uart_init();
    	while(1)
    	{
    		printf("请输入两个整数:");
    		scanf("%d%d, &a, &b);
    		printf("\r\n数据%d + %d = %d\r\n\r\n",a,b,a+b);
    		state = !state;
    		led_switch(LED0, state);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.2 编译下载

    修改 TARGET,在 INCDIRS 中添加stdio/include,在 SRCDIRS 中添加 stdio/lib。编译C文件时需要添加选项

    $(COBJS) : obj/%.o : %.c
    	$(CC) -Wall -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2
    
    • 1
    • 2
  • 相关阅读:
    【Vue指令】五分钟了解vue的数据绑定
    Day 62 django form modelform组件
    设计模式之状态模式
    网友提问:HTML CSS JS很低级吗?
    python输出HelloWorld
    JavaScript 基础第四天笔记
    OD华为机试 15
    【保姆级·创建对象】如何通过Supplier创建对象
    加密,数字签名,数字证书
    Python数值基本计算
  • 原文地址:https://blog.csdn.net/m0_63667883/article/details/134401378