I.MX6U 裸机开发19.串口通讯实验和printf scanf 重定向

摘要:UART(Universal Asynchronous Receiver/Trasmitter),通用异步串行收发器。它可以通过电平转换器和 RS - 232 电缆或外部电路提供与外部设备的串行通信能力。外部电路可以将红外信号转换为电信号(用于接收),或将电信

UART(Universal Asynchronous Receiver/Trasmitter),通用异步串行收发器。它可以通过电平转换器和 RS - 232 电缆或外部电路提供与外部设备的串行通信能力。
外部电路可以将红外信号转换为电信号(用于接收),或将电信号转换为驱动红外 LED 的信号(用于传输),以提供低速 IrDA 兼容性。

UART 支持以下几种编码格式:

NRZ(非归零)编码格式RS485 兼容的 9 位数据格式IrDA 兼容的红外低速数据率(SIR)格式

UART通讯一般使用三根线:TxD发送、RxD接收、GND接地。


其中各位含义解释:
这篇文档主要介绍了UART(通用异步收发器)数据传输中的几个关键概念:

空闲位:数据线在空闲状态时为逻辑“1”,即高电平,表示没有数据线空闲,没有数据传输。起始位:当要传输数据时,先传输一个逻辑“0”,即将数据线拉低,表示开始数据传输。数据位:数据位是实际要传输的数据,数据位数可选择5~8位,通常按照字节(8位)传输数据,低位在前,高位最后传输。奇偶校验位:这是对数据中“1”的位数进行奇偶校验用的,可以选择不使用奇偶校验功能。停止位:数据传输完成标志位,停止位的位数可以选择1位、1.5位或2位高电平,通常选择1位停止位。波特率:波特率是UART数据传输的速率,即每秒传输的数据位数,常见选择有9600、19200、115200等。

UART一般的接口电平有TTL和RS-232两种。

TTL电平标准一般开发板上都有TXD和RXD这样的引脚。这些引脚低电平表示逻辑0,高电平表示逻辑1,这就是TTL电平。RS-232电平标准RS-232采用差分线。-3 ~ -15V表示逻辑1,+3 ~ +15V表示逻辑0。

低8位保存串口接收到的数据,其他位比较少用。

低8位放要发送的数据。

串口控制寄存器。

UARTx_UCR1bit0: UART的使能位;bit14: 自动检测波特率,设置为0不使用;UARTx_UCR2bit0:软复位,0的时候复位,1时候不复位。bit1:接收使能bit2:发送使能bit5:设置数据长度,0表示7位数据位,1表示8位数据位bit6:停止位,0:1位停止位, 1:2位停止位bit7:奇偶校验位,为0是偶校验,1是奇校验bit8:校验使能UARTx_UCR3bit2:设置为1,工作在MUXED 模式。

忽略

bit7~9:设置分频值。

UART的时钟源由 CSCDR1的 UART_CLK_SEL 位设置,可以是OSC24MHz(UART_CLK_SEL=1),或 PLL3/6 分频(UART_CLK_SEL=0):


即 480MHz/6 = 80MHz,根据这个值设置分频。
本文使用PLL/6作为时钟源。

另外 CSCDR1[UART_CLK_PODF] 分频值设置1分频。
UARTx_UFCR设置的是80MHz时钟源的分频值,得到的值 RefFreq,再通过UART_UFCR、UART_UBIR、UART_UBMR三个寄存器确定串口波特率,计算公式如下:

UART1_TXD 使用的 IO 为UART1_TX_DATA, UART1_RXD 使用的IO为 UART1_RXD_DATA。
设置其复用功能:


MINI 开发板通过 USB TTL 与电脑相连。

/*** @brief UART的IO初始化*/void uart_io_init(void){// 复用为UART1_TXIOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);// 设置电气属性IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10B0);// 复用为UART1_RXIOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);// 设置电气属性IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10B0);}/*** @brief 初始化UART1*/void uart_init(UART_Type *base){// 1、初始化IOuart_io_init;// 2、初始化UARTuart_disable(base);uart_reset(base);// 3、配置UARTbase->UCR1 = 0;// 配置数据位、停止位和校验位base->UCR2 = 0;// 1接收使能 | 2发送使能 | 5数据位8位 | 14忽略RTS脚base->UCR2 |= (1 UCR3 |= (1 UFCR = 5 UBIR = 71;base->UBMR = 3124;// 4、使能UARTuart_enable(base);}/*** @brief 关闭UART1*/void uart_disable(UART_Type *base) {base->UCR1 &= ~ (1 UCR1 |= 1 UCR2 &= ~ (1 UCR2 & 0x1) == 0); /* 等待复位完成 */}/*** @brief 发送一个字符*/void uart_write(UART_Type *base, unsigned char c){// 等待发送缓冲区为空while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */UART1->UTXD = c & 0XFF; /* 发送数据 */}/*** @brief 发送字符串*/void uart_write_str(UART_Type *base, const char *str){while (*str) {uart_write(base, *str++);}}/*** @brief 读取一个字符*/unsigned char uart_read(const UART_Type *base){// 等待接收缓冲区满while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */return base->URXD;}/*** @brief 读取字符串*/void uart_read_str(const UART_Type *base, char *str, unsigned int len){unsigned int i = 0;while (i /*** @brief 初始化系统时钟*/void imx6u_clkinit(void) {// 如果PLL1_SW_CLK_SEL 当前不是 step_clkif (((CCM->CCSR >> 2) & 0x1)== 0) {// 设置备用时钟step_sel=osc_clk(24MHz)CCM->CCSR &= ~(1 CCSR |= (1 PLL_ARM = (1 CACRR = 1;// 将pll1_sw_clk切换为pll1_main_clk,即1056MHzCCM->CCSR &= ~(1 CSCDR1 &= ~(1 CSCDR1 &= ~0x3F;}

在电脑打开串口调试工具,开发板上电后,串口工具连接相应的串口,按开发板的RESET键复位。 这时串口调试工具窗口打印字符: please input char: ,输入一个字符,开发板会回显输入的字符。

从 正点原子的例程里直接拷贝从NXP移植的 uart_setbaudrate 函数。
原型如下:

/** @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);

使用方法:

// 配置波特率 115200// base->UFCR = 5 UBIR = 71;// base->UBMR = 3124;uart_setbaudrate(base, 115200, 80000000);

为了使用这个函数,需要引用数学库,修改 Makefile

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

然后加到 $(LD) 指令:

# 生成二进制文件$(TARGET).bin : $(OBJS) | obj $(LD) -T$(LINKER_SCRIPT) -o obj/$(TARGET).elf $^ $(LIBPATH) $(OBJCOPY) -O binary -S obj/$(TARGET).elf $@ $(OBJDUMP) -D -m arm obj/$(TARGET).elf > obj/$(TARGET).dis/** @description : 发送一个字符* @param - c : 要发送的字符* @return : 无*/void putc(unsigned char c){while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */UART1->UTXD = c & 0XFF; /* 发送数据 */}/** @description : 接收一个字符* @param : 无* @return : 接收到的字符*/unsigned char getc(void){while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */return UART1->URXD; /* 返回接收到的数据 */}

由于定义的函数与内置的putc会冲突, 在 Makefile 编译命令还需要修改:

$(SOBJS) : obj/%.o : %.S $(CC) -Wall -nostdlib -fno-builtin -c -O2 $(include) -o $@ $

这时编译还会报:

/tmp/ccDDezEw.s: Assembler messages:/tmp/ccDDezEw.s:42: Error: Thumb conditional instruction should be in IT block -- `addcs r5,r5,#65536'

这个错误是因为在汇编代码中使用了 Thumb 条件指令 addcs,但它没有包含在 IT (If-Then) 块中。
处理方式是在Makefile 编译命令再加上 -Wa, -mimplicit-it=thumb,用于告诉汇编器在生成 Thumb 指令时自动插入 IT (If-Then) 块。

# 编译C文件$(COBJS) : obj/%.o : %.c | obj $(CC) -Wall -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $#include "inc/main.h"#include "bsp_clk.h"#include "bsp_delay.h"#include "led.h"#include "beep.h"#include "key.h"#include "bsp_int.h"#include "bsp_epit.h"#include "bsp_keyfilter.h"#include "bsp_uart.h"#include "stdio.h"int main(void){unsigned char state = OFF;int a , b;bsp_int_init; /* 初始化中断 */imx6u_clkinit; /* 初始化系统时钟 */clk_enable; /* 使能外设时钟 */led_init; /* 初始化LED */beep_init; /* 初始化蜂鸣器 */gpt1_delay_init; /* 初始化GPT1高精度延时 */uart_init(UART1); /* 初始化UART1的IO */while(1) {printf("input 2 numbers:");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;}

来源:编程圈子

相关推荐