ARM-08-I.MX6U UART 串口
一、串口介绍串口是单片机和嵌入式 Linux 开发中最常用的外设可用于将开发板与电脑相连通过串口调试助手调试程序蓝牙、GPS、GPRS 等模块与主控通信嵌入式 Linux 中作为控制台使用I.MX6U 共有 8 个 UART主要特性兼容 TIA/EIA-232F 标准速度最高可到 5Mbit/S支持串行 IR 接口兼容 IrDA最高可到 115.2Kbit/s支持 9 位或者多节点模式RS-4851 或 2 位停止位可编程的奇偶校验奇校验和偶校验自动波特率检测最高支持 115.2Kbit/S本篇目标实现115200,8,N,1与电脑之间通信。二、配置串口寄存器时钟配置时钟源由寄存器CCM_CSCDR1的UART_CLK_SEL(bit)位选择为 0UART 时钟源为pll3_80m80MHz←一般选择此项为 1UART 时钟源为osc_clk24MHzUART_CLK_PODF(bit5:0)时钟分频值可设置 0~63对应 1~64 分频←一般设置为 1 分频最终进入 UART 的时钟为80MHz1. 选择串口引脚功能和电气属性这里选择 UART1IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0); IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0); IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10b0); IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10b0);注意不配置 UART1 的 CTS_B 和 RTS_B因为CTS/RTS 引脚用于硬件流控不是 UART 通信的必要引脚只有在需要防止数据溢出的高速或大数据量传输场景如 GSM 模块、GPS 模块、蓝牙模块等才需要配置2. 配置三个 UART 控制寄存器UARTx_UCR1位名称说明bit14ADBR自动波特率检测使能位0关闭1使能bit0UARTENUART 使能位0关闭1使能⚠️ 注意要最后配好所有东西再使能。UART1-UCR1 | (1 0); // 使能 UART最后执行UARTx_UCR2位名称说明bit14IRTS0使用 RTS 引脚功能1忽略 RTS 引脚bit8PREN奇偶校验使能位0关闭1使能bit7PROE奇偶校验模式选择0偶校验1奇校验bit6STOP停止位数量01位12位bit5WS数据位长度07位18位bit2TXEN发送使能位0关闭1打开bit1RXEN接收使能位0关闭1打开bit0SRST软件复位0软件复位 UART1复位完成自动置1UART1-UCR2 ~(1 0); // 软件复位 delay_us(1); unsigned int tmp UART1-UCR2; tmp | (1 14); // 忽略 RTS tmp ~(1 8); // 关闭奇偶校验 tmp ~(1 6); // 1 位停止位 tmp | (1 5); // 8 位数据位 tmp | (1 2); // 使能发送 tmp | (1 1); // 使能接收 UART1-UCR2 tmp;UARTx_UCR3该寄存器的第二位RXDMUXSEL 必须始终为 1手册规定位名称说明bit3TXDC发送完成标志位为1时发送缓冲(TxFIFO)和移位寄存器为空发送完成写入TxFIFO后自动清零bit0RDR数据接收标志位为1时表明至少接收到一个数据读取 UARTx_URXD 后自动清零UART1-UCR3 | (1 2); // RXDMUXSEL 置 13. 波特率配置工作频率计算PLL3 时钟 480MHz → 静态 6 分频 →80MHzUART 自身分频器由UARTx_UFCR[(bit9~bit7)]决定 → 需要1分频即b101波特率计算公式(UBMR 1) / (UBIR 1) RefFreq / (16 × BaudRate)RefFreqUART 实际工作频率80MHzBaudRate波特率UBMR寄存器 UARTx_UBMR 中的值UBIR寄存器 UARTx_UBIR 中的值例波特率 115200工作频率 80MHz右边 80000000 / (16 × 115200) 43.402设 (UBIR1) 1000则 (UBMR1) 43402所以UBIR 999UBMR 43401tmp UART1-UFCR; tmp ~(0x7 7); // 清除分频位 tmp | (0x5 7); // 设置 1 分频b101 UART1-UFCR tmp; UART1-UBIR 999; UART1-UBMR 43404; // 实际代码中用 434044. 读写函数UARTx_USR2UART 状态寄存器2位名称说明bit3TXDC发送完成标志位为1时发送缓冲和移位寄存器为空bit0RDR数据接收标志位为1时至少接收到一个数据写函数JTXD写数据寄存器void uart_send_byte(UART_Type * base, unsigned char data) { while(!(base-USR2 (1 3))); // 等待发送完成第3位为1则跳出 base-UTXD data; }读函数URXD读数据寄存器unsigned char uart_recv_byte(UART_Type * base) { unsigned char data 0; while(!(base-USR2 (1 0))); // 等待接收到数据第0位为1则跳出 data base-URXD 0xff; // 只取低8位有效数据 return data; }注意data UART1-URXD 0xff因为只有低八位才是有效数据。三、完整 uart.c 代码#include MCIMX6Y2.h #include fsl_iomuxc.h #include uart.h #include gpt.h void uart_init(UART_Type * base) { if (base UART1) { IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0); IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0); IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX, 0x10b0); IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x10b0); } else if(base UART2) { IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_UART2_RX, 0); IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_UART2_TX, 0); IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_UART2_RX, 0x10b0); IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_UART2_TX, 0x10b0); } base-UCR2 ~(1 0); // 软件复位 delay_us(1); unsigned int tmp base-UCR2; tmp | (1 14); // 忽略 RTS tmp ~(1 8); // 关闭奇偶校验 tmp ~(1 6); // 1 位停止位 tmp | (1 5); // 8 位数据位 tmp | (1 2); // 使能发送 tmp | (1 1); // 使能接收 base-UCR2 tmp; base-UCR3 | (1 2); // RXDMUXSEL 置 1 tmp base-UFCR; tmp ~(0x7 7); tmp | (0x5 7); // 1 分频 base-UFCR tmp; base-UBIR 999; base-UBMR 43404; base-UCR1 | (1 0); // 使能 UART } void uart_send_byte(UART_Type * base, unsigned char data) { while(!(base-USR2 (1 3))); base-UTXD data; } unsigned char uart_recv_byte(UART_Type * base) { unsigned char data 0; while(!(base-USR2 (1 0))); data base-URXD 0xff; return data; }四、格式化输入输出printf/scanf 移植移植原理将printf和scanf函数映射到 UART1 上之后用串口调试助手等软件就可以非常方便地和开发板通信。配套资料中的stdio 文件夹里的文件是要移植的源码从 uboot 移植stdio 中并没有实现完整版的格式化函数比如printf 不支持浮点数但基本够用移植步骤修改项在 uart.c 中添加函数void raise(int n);函数体为空即可否则会报错汇编文件扩展名改大写start.s→start.SS 会先做预处理再进行编译s 则省略预处理步骤Makefile 修改由于增加了新的源码和头文件Makefile 需要更新所有引用.s的地方改成.S编译选项增加-Wa,-mimplicit-itthumb –nostdlib否则报错thumb conditional instruction should be in IT block增加源程序路径和头文件路径并添加链接目录如果报错缺少 putc 和 getc自己写void putc(unsigned char data) { while(!(UART1-USR2 (1 3))); UART1-UTXD data; } unsigned char getc(void) { unsigned char data 0; while(!(UART1-USR2 (1 0))); data UART1-URXD 0xff; return data; }五、Makefiletarget uart cross_compiler arm-linux-gnueabihf- cc $(cross_compiler)gcc ld $(cross_compiler)ld objcopy $(cross_compiler)objcopy objdump $(cross_compiler)objdump incdirs bsp sdk stdio/include srcdirs bsp project stdio/lib include $(patsubst %, -I%, $(incdirs)) cfiles $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.c)) sfiles $(foreach dir, $(srcdirs), $(wildcard $(dir)/*.S)) cfilenodir $(notdir $(cfiles)) sfilenodir $(notdir $(sfiles)) cobjs $(patsubst %, obj/%, $(cfilenodir:.c.o)) sobjs $(patsubst %, obj/%, $(sfilenodir:.S.o)) objs $(cobjs) $(sobjs) VPATH $(srcdirs) gcc_lib -lgcc -L /home/linux/tools/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4 $(target).bin : $(objs) $(ld) -Timx6ull.lds -o$(target).elf $^ $(gcc_lib) $(objcopy) -O binary -S -g $(target).elf $ $(objdump) -D $(target).elf $(target).dis $(sobjs) : obj/%.o : %.S mkdir -p obj $(cc) -Wa,-mimplicit-itthumb -nostdlib -fno-builtin -c $(include) -o $ $ $(cobjs) : obj/%.o : %.c mkdir -p obj $(cc) -Wa,-mimplicit-itthumb -nostdlib -fno-builtin -c $(include) -o $ $ .PHONY : clean clean: rm -rf $(objs) $(target).elf $(target).bin $(target).dis load: ./imxdownload ./$(target).bin /dev/sdb六、main.c 实战串口命令控制 LED 和蜂鸣器#include MCIMX6Y2.h #include beep.h #include clk.h #include core_ca7.h #include epit.h #include fsl_iomuxc.h #include gpt.h #include irq.h #include key.h #include led.h #include stdio.h #include uart.h int main(void) { char data[50] {0}; clk_init(); led_init(); beep_init(); gpt1_init(); uart_init(UART1); while (1) { printf(input ur command:); scanf(%s, data); if (strncmp(data, ledon, 5) 0) { led_on(); printf(open led already\r\n); } else if (strncmp(data, ledoff, 6) 0) { led_off(); printf(close led already\r\n); } else if (strncmp(data, beepon, 6) 0) { beep_on(); printf(open beep already\r\n); } else if (strncmp(data, beepoff, 7) 0) { beep_off(); printf(close beep already\r\n); } else { printf(invalid command\r\n); } } return 0; }效果下载后通过串口调试助手输入命令ledon/ledoff/beepon/beepoff开发板即可响应控制。七、关键寄存器速查表寄存器位功能CCM_CSCDR1UART_CLK_SEL时钟源选择0pll3_80m1osc_clkCCM_CSCDR1UART_CLK_PODF时钟分频0~63 → 1~64分频UCR1bit0 UARTENUART 使能UCR1bit14 ADBR自动波特率检测使能UCR2bit0 SRST软件复位UCR2bit1 RXEN接收使能UCR2bit2 TXEN发送使能UCR2bit5 WS数据位07位18位UCR2bit6 STOP停止位01位12位UCR2bit7 PROE奇偶校验模式0偶1奇UCR2bit8 PREN奇偶校验使能UCR2bit14 IRTSRTS忽略位UCR3bit2 RXDMUXSEL必须始终为1UCR3bit3 TXDC发送完成标志UCR3bit0 RDR数据接收标志UFCRbit9~bit7UART 自身分频器UBIR-波特率计算分子UBMR-波特率计算分母USR2bit3 TXDC发送完成标志写函数轮询USR2bit0 RDR数据接收标志读函数轮询UTXD-发送数据寄存器URXD-接收数据寄存器只取低8位