【ARM】IMX6UL串口通信
串口这个东西很重要,罗里吧嗦的就不说了
参考资料:IMX6U参考手册,正点原子嵌入式linux驱动开发指南
1.UART简介
UART作为串口的一种,工作原理就是数据一位一位的进行传输,发送和接收各用一条线,因为UART接口与外界相连最少需要三个线:TXD、RXD、GND。UART的通信格式如下:
空闲位: 数据线在空闲状态的时候为逻辑"1"状态,表示没有数据线空闲,没有数据传输。
起始位: 当要传输数据的时候先传输一个逻辑0,也就是将数据线拉低,表示开始数据传输。
数据位: 数据位就是实际要传输的数据,数据位数可选择5~8,一般按照字节传输数据,一个字节 8 位,因此数据位通常是 8 位的。低位在前,先传输,高位最后传输。
奇偶校验位: 这是对数据中“1”的位数进行奇偶校验用的,可以不使用奇偶校验功能。
停止位: 数据传输完成标志位,停止位的位数可以选择 1 位、 1.5 位或 2 位高电平,一般都
选择 1 位停止位。
I.MX6U 一共有 8 个 UART,其主要特性如下:
①、兼容 TIA/EIA-232F 标准,速度最高可到 5Mbit/S。
②、支持串行 IR 接口,兼容 IrDA,最高可到 115.2Kbit/s。
③、支持 9 位或者多节点模式(RS-485)。
④、 1 或 2 位停止位。
⑥、可编程的奇偶校验(奇校验和偶校验)。
⑦、自动波特率检测(最高支持 115.2Kbit/S)。
UART的时钟源是由寄存器CCM_CSCDR1的UART_CLK_SEL位确定的,当为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
2.和UART相关的寄存器
- UARTx_UCR1(x=1~8)
- UARTx_UCR2
- UARTx_UCR3
- UARTx_USR2
- UARTx_UFCR
- UARTx_UBIR
- UARTx_UBMR
- UARTx_URXD
- UARTx_UTXD
UARTx_UCR1
寄存器 UARTx_UCR1 我们用到的重要位如下:
**ADBR(bit14):**自动波特率检测使能位,为 0 的时候关闭自动波特率检测,为 1 的时候使
能自动波特率检测。
** UARTEN(bit0): ** UART 使能位,为 0 的时候关闭 UART,为 1 的时候使能 UART
UARTx_UCR2
寄存器 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
RXDMUXSEL(bit2): 始终为1
UARTx_USR2
寄存器 UARTx_USR2 用到的重要位如下:
**TXDC(bit3):**发送完成标志位,为 1 的时候表明发送缓冲(TxFIFO)和移位寄存器为空,也就是发送完成,向 TxFIFO 写入数据此位就会自动清零。
**RDR(bit0):**数据接收标志位,为 1 的时候表明至少接收到一个数据,从寄存器UARTx_URXD 读取数据接收到的数据以后此为会自动清零。
UARTx_UFCR 、 UARTx_UBIR 和 UARTx_UBMR
UARTx_UFCR 中我们要用到的是位 RFDIV(bit9:7),用来设置参考时钟分频
这三个寄存器设置UART的波特率,波特率的计算公式如下:
Ref Freq: 经过分频以后进入 UART 的最终时钟频率。
UBMR: 寄存器 UARTx_UBMR 中的值。
UBIR: 寄存器 UARTx_UBIR 中的值。
比如现在要设置 UART 波特率为 115200,那么可以设置 RFDIV 为5(0b101),也就是 1 分频,因此 Ref Freq=80MHz。设置 UBIR=71, UBMR=3124,根据上面的公式可以得到:
UARTx_URXD 和 UARTx_UTXD
UARTx_URXD 和 UARTx_UTXD,这两个寄存器分别为 UART 的接收和发送数据寄存器,这两个寄存器的低八位为接收到的和要发送的数据。读取寄存器UARTx_URXD 即可获取到接收到的数据,如果要通过 UART 发送数据,直接将数据写入到寄存器 UARTx_UTXD 即可。
3.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 的数据收发操作
下面是相关的函数
/** @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)
{}
【ARM】IMX6UL串口通信相关推荐
- arm linux串口控制led,通信程序设计 - Linux下ARM和单片机的串口通信设计
3 通信程序设计 ARM 与单片机的串口通信程序包括两方面: 一方面是作为上位机的ARM 的串口通信程序,另一方面是作为下位机的单片机的串口通信程序.在通信之前必须制定合理的通信协议以保证通信的可靠性 ...
- TQ2440(ARM)和SIM300(GPRS模块)串口通信
TQ2440(ARM)和SIM300(GPRS模块)串口通信 三月 15th, 2012 Linux平台开发 • 毕业论文 • 编程技术 1 Comment 最近在做串口通信这快遇到了很大的麻烦.现在 ...
- ARM内核单片机的串口通信(UART)使用(数据的收发)
ARM内核单片机的串口通信(UART)使用 进入正题: 首先,要明白使用UART进行通信,无论收发数据,传输的都是16进制.而中文.英文.标点符号其本质都是16进制,只是一个中文需要用两个字节表示,一 ...
- ARM芯片(S5PV210芯片)——串口通信详解
1.电子通信的基本概念 1.1.同步通信和异步通信 1.同步通信:通信双方有统一的工作节拍,一般需要发送方给接收方发送信息的同时发送时钟信号,接收方根据发送方给它的时钟信号来安排自己的工作节奏.同步通 ...
- NXP(I.MX6uLL) UART串口通信原理————这个未复习
参考:Linux NXP (I.MX6uLL) UART串口通信原理 作者:一只青木呀 发布时间: 2020-09-20 16:48:33 网址:https://blog.csdn.net/weixi ...
- linux 嵌入式串口通信设计目的,基于linux的嵌入式串口通信.doc
PAGE 天津电子信息职业技术学院 <嵌入式软件编程>课程报告 课程名称:基于linux的嵌入式串口通信 课程代码: 115229 姓 名: 甘琦 学 号: 48 专 业: 物联网应用技术 ...
- linux 嵌入式串口通信,基于linux的嵌入式串口通信综述.doc
PAGE 天津电子信息职业技术学院 <嵌入式软件编程>课程报告 课程名称:基于linux的嵌入式串口通信课程代码: 115229 姓 名: 甘琦 学 号: 48 专 业: 物联网应用技术 ...
- 串口通信Serial
基础知识 ASCII 表 | 菜鸟教程 (runoob.com) hex取值范围:不同编码有不同的解码范围,超过范围则无法解码 串口通信分类 一.并行通信 8位数据同时通过并行线进行传送,传输速率较高 ...
- GD32串口通信注意事项
GD32串口通信 虽说GD32与stm32采用同样的ARM® Cortex®-M3内核,而且GD32很大程度上可兼容stm32的程序,但是也存在着一些差异:所以为了使用上减少出错,使用GD32时尽量采 ...
最新文章
- SpringBoot项目打包war部署到服务器去掉项目名所遇到的坑
- Linus:Linux太烂,我要删了它,你们用Windows XP吧!
- Android View measure(0,0)的作用
- 计算机不会输入函数怎么办,函数不正确_电脑上文件打不开,显示函数不正确怎么解决?...
- 用BlockingExecutor限制任务提交
- 划分vlan实验心得体会_思科:相同vlan,不同交换机之间的通信
- ThreadLocal类学习笔记
- ASP.NET ActionPack
- idea/eclipse/vscode同时搜索多个文件类型
- 【Proteus仿真8086】往8086 内存中写入数据
- Tableau Desktop【包含各个版本】
- vpp flowprobe
- 连通区域的边界点程序
- 科研狗工具大合集,赶紧集合看过来
- 如何在各种非三星电脑上安装Samsung Notes三星笔记
- Python销售管理系统
- 【云原生】风云暗涌的时代,DBA们的利刃出鞘了
- android camera 对焦大小,Android camera2对焦设置
- 小程序的影响到底有多大?
- s5pv210的工作模式