Linux学习笔记17---UART 串口通信实验

server/2025/2/8 12:12:01/
        不管是单片机开发还是嵌入式 Linux 开发,串口都是最常用到的外设。可以通过串口将开发板与电脑相连,然后在电脑上通过串口调试助手来调试程序。还有很多的模块,比如蓝牙、 GPS、 GPRS 等都使用的串口来与主控进行通信的,在嵌入式 Linux 中一般使用串口作为控制台,所以掌握串口是必备的技能。本章我们就来学习如何驱动 I.MX6U 上的串口,并使用串口和电脑进行通信。

1、MX6U 串口简介

        1.1 UART 通信格式

        串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距离远,但是速度相对会低,串口是一种很常用的工业接口。I.MX6U 自带的 UART 外设就是串口的一种,UART 全称是 UniversalAsynchronous Receiver/Trasmitter ,也就是异步串行收发器。既然有异步串行收发器,那肯定也有同步串行收发器,学过 STM32 的同学应该知道, STM32除 了有 UART 外,还有 另外一 个叫做 USART 的东西 。 USART 的 全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,也就是同步 / 异步串行收发器。相比 UART 多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线。一般 USART 是可以作为 UART
使用的,也就是不使用其同步的功能。
        UART 作为串口的一种,其工作原理也是将数据一位一位的进行传输,发送和接收各用一条线,因此通过 UART 接口与外界相连最少只需要三条线: TXD( 发送 ) RXD( 接收 ) GND( 地 线) 。图 21.1.1.1 就是 UART 的通信格式:
21.1.1.1 中各位的含义如下:
        空闲位:数据线在空闲状态的时候为逻辑“ 1 ”状态,也就是高电平,表示没有数据线空闲,没有数据传输。
        起始位:当要传输数据的时候先传输一个逻辑“ 0 ”,也就是将数据线拉低,表示开始数据传输。
        数据位:数据位就是实际要传输的数据,数据位数可选择 5~8 位,我们一般都是按照字节传输数据的,一个字节 8 位,因此数据位通常是 8 位的。低位在前,先传输,高位最后传输。
        奇偶校验位:这是对数据中“ 1 ”的位数进行奇偶校验用的,可以不使用奇偶校验功能。
        停止位:数据传输完成标志位,停止位的位数可以选择 1 位、 1.5 位或 2 位高电平,一般都选择 1 位停止位。
        波特率:波特率就是 UART 数据传输的速率,也就是每秒传输的数据位数,一般选择 9600 、19200、 115200 等。

2、MX6U UART 简介

        上面介绍了 UART 接口,现在来具体看一下 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)
        I.MX6U 的 UART 功能很多,但是我们本章就只用到其最基本的串口功能,关于 UART 其它功能的介绍请参考《I.MX6ULL 参考手册》第 3561 页的“ Chapter 55 Universal Asynchronous Receiver/Transmitter(UART)”章节。
        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
        接下来看一下 UART 几个重要的寄存器,第一个就是 UART 的控制寄存器 1 ,即 UARTx_UCR1(x=1~8),此寄存器的结构如图 21.1.2.1 所示:
图 21.1.2.1 寄存器 UARTx_UCR1 结构
寄存器 UARTx_UCR1 我们用到的重要位如下:
        ADBR(bit14):自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使能自动波特率检测。
        UARTEN(bit0) UART 使能位,为 0 的时候关闭 UART ,为 1 的时候使能 UART
        接下来看一下 UART 的控制寄存器 2 ,即: UARTx_UCR2 ,此寄存器结构如图 21.1.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 的时候关闭 UART 的发送功能,为 1 的时候打开 UART的发送功能。
        RXEN(bit1):接收使能位,为 0 的时候关闭 UART 的接收功能,为 1 的时候打开 UART的接收功能。
        SRST(bit0):软件复位,为 0 的是时候软件复位 UART ,为 1 的时候表示复位完成。复位完成以后此位会自动置 1 ,表示复位完成。此位只能写 0 ,写 1 会被忽略掉。
        接下来看一下 UARTx_UCR3 寄存器,此寄存器结构如图 21.1.2.3 所示:
图 21.1.2.3 UARTx_UCR3 寄存器结构体
        本章实验就用到了寄存器 UARTx_UCR3 中的位 RXDMUXSEL(bit2) ,这个位应该始终为 1 ,这个在《I.MX6ULL 参考手册》第 3624 页有说明。
        接下来看一下寄存器 UARTx_USR2 ,这个是 UART 的状态寄存器 2 ,此寄存器结构如图21.1.2.4 所示:

寄存器 UARTx_USR2 用到的重要位如下:
        TXDC(bit3):发送完成标志位,为 1 的时候表明发送缓冲 (TxFIFO) 和移位寄存器为空,也就是发送完成,向 TxFIFO 写入数据此位就会自动清零。
        RDR(bit0):数据接收标志位,为 1 的时候表明至少接收到一个数据,从寄存器UARTx_URXD 读取数据接收到的数据以后此位会自动清零。
        接下来看一下寄存器 UARTx_UFCR UARTx_UBIR UARTx_UBMR ,寄存器UARTx_UFCR 中我们要用到的是位 RFDIV(bit9:7) ,用来设置参考时钟分频,设置如表 21.1.2.1 所示:

 通过这三个寄存器可以设置 UART 的波特率,波特率的计算公式如下:

                Ref Freq:经过分频以后进入 UART 的最终时钟频率。
                UBMR:寄存器 UARTx_UBMR 中的值。
                UBIR:寄存器 UARTx_UBIR 中的值。
        通过 UARTx_UFCR RFDIV 位、 UARTx_UBMR UARTx_UBIR 这三者的配合即可得到我们想要的波特率。比如现在要设置 UART 波特率为 115200 ,那么可以设置 RFDIV 为 5(0b101),也就是 1 分频,因此 Ref Freq=80MHz 。设置 UBIR=71 UBMR=3124 ,根据上面的公式可以得到:

        最后来看一下寄存器 UARTx_URXD UARTx_UTXD ,这两个寄存器分别为 UART 的接收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器UARTx_URXD 即可获取到接收到的数据,如果要通过 UART 发送数据,直接将数据写入到寄存器 UARTx_UTXD 即可。
        关于 UART 的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《 I.MX6ULL 参考手册》第 3608 页的 55.15 小节。本章我们使用 I.MX6U UART1 来完成开发板与电脑串口调试助手之间串口通信, UART1 的配置步骤如下:
1 、设置 UART1 的时钟源
        设置 UART 的时钟源为 pll3_80m ,设置寄存器 CCM_CSCDR1 UART_CLK_SEL 位为 0 即可。
2 、初始化 UART1
        初始化 UART1 所使用 IO ,设置 UART1 的寄存器 UART1_UCR1~UART1_UCR3 ,设置内容包括波特率,奇偶校验、停止位、数据位等等。
4 、使能 UART1
        UART1 初始化完成以后就可以使能 UART1 了,设置寄存器 UART1_UCR1 的位 UARTEN为 1
5 、编写 UART1 数据收发函数
        编写两个函数用于 UART1 的数据收发操作。

3、硬件原理分析

本试验用到的资源如下:
①、一个 LED 灯: LED0
②、串口 1
V2.4 以前版本 I.MX6U-ALPHA 开发板串口 1 硬件原理图如图 21.2.1 所示:

        在做实验之前需要用 USB 串口线将串口 1 和电脑连接起来硬件连接设置好以后就可以开始软件编写了,本章实验我们初始化好 UART1,然后等待SecureCRT 给开发板发送一个字节的数据,开发板接收到 SecureCRT 发送过来的数据以后在通过串口 1 发送给 SecureCRT。

4、实验程序编写

本章实验在上一章例程的基础上完成,更改工程名字为“ uart ”,然后在 bsp 文件夹下创建名为“uart ”的文件夹,然后在 bsp/uart 中新建 bsp_uart.c bsp_uart.h 这两个文件。在 bsp_uart.h中输入如下内容:
#ifndef _BSP_UART_H
#define _BSP_UART_H
#include "imx6ul.h"/* 函数声明 */
void uart_init(void);
void uart_io_init(void);
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);
void raise(int sig_nr);#endif
文件 bsp_uart.h 内容很简单,就是一些函数声明。继续在文件 bsp_uart.c 中输入如下所示内容:
#include "bsp_uart.h"/** @description : 初始化串口1,波特率为115200* @param		: 无* @return		: 无*/
void uart_init(void)
{/* 1、初始化串口IO 			*/uart_io_init();/* 2、初始化UART1  			*/uart_disable(UART1);	/* 先关闭UART1 		*/uart_softreset(UART1);	/* 软件复位UART1 		*/UART1->
UCR1 = 0;		/* 先清除UCR1寄存器 *//** 设置UART的UCR1寄存器,关闭自动波特率* bit14: 0 关闭自动波特率检测,我们自己设置波特率*/UART1->UCR1 &= ~(1<<14);/** 设置UART的UCR2寄存器,设置内容包括字长,停止位,校验模式,关闭RTS硬件流控* bit14: 1 忽略RTS引脚* bit8: 0 关闭奇偶校验* bit6: 0 1位停止位* bit5: 1 8位数据位* bit2: 1 打开发送* bit1: 1 打开接收*/UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);/** UART1的UCR3寄存器* bit2: 1 必须设置为1!参考IMX6ULL参考手册3624页*/UART1->UCR3 |= 1<<2; /** 设置波特率* 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)) * 如果要设置波特率为115200,那么可以使用如下参数:* Ref Freq = 80M 也就是寄存器UFCR的bit9:7=101, 表示1分频* UBMR = 3124* UBIR =  71* 因此波特率= 80000000/(16 * (3124+1)/(71+1))=80000000/(16 * 3125/72) = (80000000*72) / (16*3125) = 115200*/UART1->UFCR = 5<<7; //ref freq等于ipg_clk/1=80MhzUART1->UBIR = 71;UART1->UBMR = 3124;#if 0uart_setbaudrate(UART1, 115200, 80000000); /* 设置波特率 */
#endif/* 使能串口 */uart_enable(UART1);
}/** @description : 初始化串口1所使用的IO引脚* @param		: 无* @return		: 无*/
void uart_io_init(void)
{/* 1、初始化IO复用 * UART1_RXD -> UART1_TX_DATA* UART1_TXD -> UART1_RX_DATA*/IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0);	/* 复用为UART1_TX */IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0);	/* 复用为UART1_RX *//* 2、配置UART1_TX_DATA、UART1_RX_DATA的IO属性 *bit 16:0 HYS关闭*bit [15:14]: 00 默认100K下拉*bit [13]: 0 keeper功能*bit [12]: 1 pull/keeper使能*bit [11]: 0 关闭开路输出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 110 驱动能力R0/6*bit [0]: 0 低转换率*/IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);
}/** @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)
{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;/* get the approximately maximum divisor */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;/* numerator ranges from 1 ~ 7 * 64k *//* denominator ranges from 1 ~ 64k */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;}/* Compare the difference between baudRate_Bps and calculated baud rate.* Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).* baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / divider)/ denominator).*/tempNumerator = srcclock_hz;tempDenominator = (numerator << 4);divisor = 1;/* get the approximately maximum divisor */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); //要先写UBIR寄存器,然后在写UBMR寄存器,3592页 base->UBMR = UART_UBMR_MOD(numerator / divider - 1);}
}/** @description : 关闭指定的UART* @param - base: 要关闭的UART* @return		: 无*/
void uart_disable(UART_Type *base)
{base->UCR1 &= ~(1<<0);	
}/** @description : 打开指定的UART* @param - base: 要打开的UART* @return		: 无*/
void uart_enable(UART_Type *base)
{base->UCR1 |= (1<<0);	
}/** @description : 复位指定的UART* @param - base: 要复位的UART* @return		: 无*/
void uart_softreset(UART_Type *base)
{base->UCR2 &= ~(1<<0); 			/* UCR2的bit0为0,复位UART  	  	*/while((base->UCR2 & 0x1) == 0); /* 等待复位完成 					*/
}/** @description : 发送一个字符* @param - c	: 要发送的字符* @return		: 无*/
void putc(unsigned char c)
{while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */UART1->UTXD = c & 0XFF; 				/* 发送数据 */
}/** @description : 发送一个字符串* @param - str	: 要发送的字符串* @return		: 无*/
void puts(char *str)
{char *p = str;while(*p)putc(*p++);
}/** @description : 接收一个字符* @param 		: 无* @return		: 接收到的字符*/
unsigned char getc(void)
{while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */return UART1->URXD;				/* 返回接收到的数据 */
}/** @description : 防止编译器报错* @param 		: 无* @return		: 无*/
void raise(int sig_nr) 
{}
        文件 bsp_uart.c 中共有 10 个函数,我们依次来看一下这些函数都是做什么的,第一个函数是 uart_init ,这个函数是 UART1 初始化函数,用于初始化 UART1 相关的 IO 、并且设置 UART1 的波特率、字长、停止位和校验模式等,初始化完成以后就使能 UART1 。第二个函数是 uart_io_init,用于初始化 UART1 所使用的 IO 。第三个函数是 uart_setbaudrate ,这个函数是从 NXP 官方的 SDK 包里面移植过来的,用于设置波特率。我们只需将要设置的波特率告诉此函 数,此函数就会使用逐次逼近方式来计算出寄存器 UART1_UFCR FRDIV 位、寄存器 UART1_UBIR 和寄存器 UART1_UBMR 这三个的值。第四和第五这两个函数为 uart_disable 和 uart_enable,分别是使能和关闭 UART1 。第 6 个函数是 uart_softreset ,用于软件复位指定的 UART 。 第七个函数是putc ,用于通过 UART1 发送一个字节的数据。第八个函数是 puts ,用于通过 UART1 发送一串数据。第九个函数是 getc ,用于通过 UART1 获取一个字节的数据,最后一个函数是 raise,这是一个空函数,防止编译器报错。
        最后在 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"/** @description	: main函数* @param 		: 无* @return 		: 无*/
int main(void)
{unsigned char a=0;unsigned char state = OFF;int_init(); 				/* 初始化中断(一定要最先调用!) */imx6u_clkinit();			/* 初始化系统时钟 			*/delay_init();				/* 初始化延时 			*/clk_enable();				/* 使能所有的时钟 			*/led_init();					/* 初始化led 			*/beep_init();				/* 初始化beep	 		*/uart_init();				/* 初始化串口,波特率115200 */while(1)				{	puts("Please enter 1 character:");a=getc();putc(a);	//回显功能puts("\r\n");//显示输入的字符puts("The characters you entered are:");putc(a);puts("\r\n\r\n");state = !state;led_switch(LED0,state);}return 0;
}
        第 5 行调用函数 uart_init 初始化 UART1 ,最终在 while 循环里面获取串口接收到的数据,并且将获取到的数据通过串口打印出来。

5、编写 Makefile 和链接脚本

Makefile 文件中输入如下内容:
CROSS_COMPILE 	?= arm-linux-gnueabihf-
TARGET		  	?= uartCC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld
OBJCOPY 		:= $(CROSS_COMPILE)objcopy
OBJDUMP 		:= $(CROSS_COMPILE)objdumpLIBPATH			:= -lgcc -L -lgcc -L /usr/lib/gcc-cross/arm-linux-gnueabihf/11INCDIRS 		:= imx6ul \bsp/clk \bsp/led \bsp/delay  \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimer \bsp/keyfilter \bsp/uart SRCDIRS			:= project \bsp/clk \bsp/led \bsp/delay \bsp/beep \bsp/gpio \bsp/key \bsp/exit \bsp/int \bsp/epittimer \bsp/keyfilter \bsp/uart INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)VPATH			:= $(SRCDIRS).PHONY: clean$(TARGET).bin : $(OBJS)$(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH)$(OBJCOPY) -O binary -S $(TARGET).elf $@$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis$(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 $@ $<clean:rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)
上述的 Makefile 文件内容和上一章实验的区别不大。将 TARGET uart ,在 INCDIRS 和 SRCDIRS 中加入“ bsp/uart ”。但是,相比上一章中的 Makefile 文件,本章实验的 Makefile 有两处重要的改变:
  • ①、本章 Makefile 文件在链接的时候加入了数学库, 因为在 bsp_uart.c 中有个函数 uart_setbaudrate,在此函数中使用到了除法运算,因此在链接的时候需要将编译器的数学库也链 接进来。第9行的变量LIBPATH就是数学库的目录,在第56行链接的时候使用了变量LIBPATH。在后面的学习中,我们常常要用到一些第三方库,那么在连接程序的时候就需要指定这些第三方库所在的目录,Makefile 在链接的时候使用选项“-L”来指定库所在的目录,比如“示例代码 21.4.1”中第 9 行的变量 LIBPATH 就是指定了我们所使用的编译器库所在的目录。

注意第9行的引用地址:

LIBPATH			:= -lgcc -L /usr/lib/gcc-cross/arm-linux-gnueabihf/11

-lgcc 代表链接器将连接GCC的支持库libgcc.a 

后面的接的是你的libgcc.a 库所在的目录,我的wsl的libgcc.a 所在目录为“/usr/lib/gcc-cross/arm-linux-gnueabihf/11”,因此如上所写,需要根据自己电脑的实际目录进行编写,要不然会报错。

  • ②、在第 61 行和 64 行中,加入了选项“-fno-builtin”,否则编译的时候提示“putc”、“puts” 这两个函数与内建函数冲突,错误信息如下所示:
warning: conflicting types for built-in function ‘putc’
warning: conflicting types for built-in function ‘puts’
        在编译的时候加入选项“-fno-builtin ”表示不使用内建函数,这样我们就可以自己实现 putc 和 puts 这样的函数了。
        链接脚本保持不变。

6、编译下载

使用 Make 命令编译代码,编译成功以后使用软件 imxdownload2 将编译完成的 uart.bin 文件生成可执行的img文件,命令如下:

make
./imxdownload2 uart.bin

如果  imxdownload2无权限,可用以下命令添加权限

chmod 777 imxdownload2

编译如图:

 利用Win32DiskImager软件将load.img执行文件写入SD卡,SD卡插入开发板上即可正常运行。

烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。打开一个串口助手,设置好相应的串口参数,连接上,根据提示输入一个字符,这个输入的字符就会通过串口发送给开发板,开发板接收到字符以后就会通过串口提示你接收到的字符是什么。 如图 所示:

至此, I.MX6U 的串口 1 就工作起来了,以后我们就可以通过串口来调试程序。但是本章只实现了串口最基本的收发功能,如果我们要想使用格式化输出话就不行了,比如最常用的printf 函数,下一章就讲解如何移植 printf 函数。

7、例程

【免费】Linux学习笔记17-UART串口通信实验例程资源-CSDN文库


http://www.ppmy.cn/server/165949.html

相关文章

电脑运行黑屏是什么原因?原因及解决方法

电脑运行黑屏是指电脑在正常开机或使用过程中&#xff0c;突然出现屏幕变黑&#xff0c;无法显示任何内容的现象。这种现象可能会给用户带来很多不便&#xff0c;甚至造成数据丢失或硬件损坏。那么&#xff0c;电脑运行黑屏是什么原因呢&#xff1f;下面我们将分析几种可能的原…

Android 中实现 PDF 预览三种方式

目录 1. 使用第三方库 PdfRenderer&#xff08;适用于 Android 5.0 及以上&#xff09; 步骤&#xff1a;2. 使用第三方库 MuPDF步骤&#xff1a;3. 使用第三方库 PdfiumAndroid步骤&#xff1a; 1. 使用第三方库 PdfRenderer&#xff08;适用于 Android 5.0 及以上&#xff09…

996引擎-怪物:Lua 刷怪+清怪+自动拾取

996引擎-怪物:Lua 刷怪+清怪+自动拾取 文件位置NPC脚本参考资料文件位置 文件位置服务端D:\996M2-lua\MirServer-lua\Mir200客户端D:\996M2-lua\996M2_debug\devNPC脚本 服务端\Mir200\Envir\Market_Def\test\test爆率-3.lua -- NPC入口函数 function main(player)-- 获取玩…

【dotnet】安全编码规范

本文将从代码安全、数据安全、身份安全、通信安全四个维度&#xff0c;总结15个关键设计原则&#xff0c;并附典型场景的解决方案与代码示例&#xff0c;助你规避90%的安全风险&#xff1a; 一、代码安全&#xff1a;构建安全防线 1. 输入验证&#xff1a;第一道屏障 漏洞场景…

C++字符串相关内容

字符串 字符串&#xff0c;本质上是一个接一个字符的一组字符。字母、数字、符号等。 const char* 字符串名 字符后面会有一个空终止符&#xff0c;为0。 字符串从指针的内存地址开始&#xff0c;然后继续下去&#xff0c;直到它碰到0&#xff0c;然后意识到字符串终止了。 …

零基础Vue入门6——Vue router

本节重点&#xff1a; 路由定义路由跳转 前面几节学习的都是单页面的功能&#xff08;都在专栏里面https://blog.csdn.net/zhanggongzichu/category_12883540.html&#xff09;&#xff0c;涉及到项目研发都是有很多页面的&#xff0c;这里就需要用到路由&#xff08;vue route…

【Docker】 Manifest与Buildx:多架构镜像管理的解析与实践

一.manifest的概述 manifest包含了镜像的层、标签、作者等关键信息&#xff0c;并支持多架构镜像的管理。通过Manifest List&#xff0c;开发者能够为同一应用提供适用于不同架构的镜像&#xff0c;从而确保其在各类平台上的兼容性。实际上是把不同操作系统和架构打包成独立的一…

组学数据分析实操系列 | (二)一键实现MaxQuant非标定量蛋白质组学搜库结果的生信分析

前言 上一篇我们介绍了如何通过MaxQuant软件对非标定量蛋白质组学原始数据进行分析得到搜库结果&#xff0c;下一步该怎么做呢&#xff1f;今天和大家分享一个非常实用的工具——LFQ-Analyst。该工具由蒙纳士大学的组学和生信平台开发&#xff0c;是一个交互式Web应用程序&…