当前位置: 首页 > news >正文

[IMX] 05.串口 - UART

目录

1.通信格式

2.电平标准

3.IMX UART 模块

4.时钟寄存器 - CCM_CSCDR1

5.控制寄存器

5.1.UART_UCR1

5.2.UART_UCR2

5.3.UART_UCR3

6.状态寄存器

6.1.UART_USR1

6.2.UART_USR2

7.FIFO 控制寄存器 - UART_UFCR

8.波特率寄存器

8.1.分母 - UART_UBIR

8.2.分子 - UART_UBMR

9.波特率计算

10.接收数据寄存器 - UART_URXD

11.发送数据寄存器 - UART_UTXD

12.代码实现


串口全称串行接口,也称为 COM 接口,串行接口指的是数据一个接一个的按顺序传输

串口的通信线路很简单,使用两条线即可实现双向通信,一条用于发送,一条用于接收

串口是一种常用的工业接口,通信的距离较远,但是速度相对较低,I.MX6U 自带的 UART 外设就是串口的一种

UART 全称是 UniversalAsynchronous Receiver/Trasmitter,即异步串行收发器

USART 全称是 Universal Synchronous/Asynchronous Receiver/Transmitter,即同步/异步串行收发器,相比 UART 多了一个同步的功能,在硬件上体现出来的就是多了一条时钟线

一般 USART 可以作为 UART 使用的,即不使用其同步的功能

1.通信格式

UART 作为串口的一种,其工作原理是将数据按一位接一位的进行传输,发送和接收各用一 条线,因此通过 UART 接口与外界相连最少需要三条线:TXD (发送)、RXD (接收) 和 GND(地线)

串口的通信格式如下所示:

图中各个位的含义如下:

  • 空闲位:数据线在空闲状态时为逻辑 “1” 状态(高电平),表示没有数据线空闲,没有数据传输;

  • 起始位:当要传输数据时先传输一个逻辑 “0”,也就是将数据线电平拉低,表示开始数据传输;

  • 数据位:数据位就是实际要传输的数据,数据位可选择 5~8 位,一般按照字节传输数据,一个字节占 8 位,因此数据位通常为 8 位,低位在前,先传输,高位最后传输;

  • 奇偶校验位:对数据中为 “1” 的位进行奇偶校验,可以不使用奇偶校验功能;

  • 停止位:数据传输完成标志位,停止位的位数可以选择 1 位、1.5 位或 2 位的高电平,一般选择 1 位停止位;

  • 波特率:波特率是 UART 数据传输的速率,即每秒传输的数据位数,一般选择 9600、19200、115200 等;

2.电平标准

UART 接口电平有 TTL 和 RS-232 两种标准

一般开发板上都有 TXD 和 RXD 引脚,引脚的低电平表示逻辑 0,高电平表示逻辑 1,这个就是 TTL 电平

RS-232 采用差分线,-3 ~ -15V 表示逻辑 1,+3 ~ +15V 表示逻辑 0

下图所示的就是 TTL 电平的接口:

上面图片中的模块为 USB 转 TTL 模块,TTL 接口部分有 VCC、GND、RXD、TXD、 RTS 和 CTS 引脚,使用时通过杜邦线和其他模块的 TTL 接口相连即可

RS-232 电平标准需要使用 DB9 接口,I.MX6U-ALPHA 开发板上的 COM3 (UART3) 口就是 RS-232 接口:

由于现在的电脑都没有 DB9 接口了,取而代之的是 USB 接口,所以就催生出了很多 USB 转串口 TTL 芯片,比如 CH340、PL2303 等,通过这些芯片可以实现串口 TTL 转 USB

I.MX6U-ALPHA 开发板使用 CH340 芯片完成 UART1 和电脑之间的连接,只需要一条USB 线即可:

3.IMX UART 模块

I.MX6U 一共有 8 个 UART 模块,主要特性如下:

  • 兼容 TIA/EIA-232F 标准,速度最高可到 5Mbit/s;

  • 支持串行 IR 接口,兼容 IrDA,最高可到 115.2Kbit/s;

  • 支持 9 位或者多节点模式 (RS-485);

  • 1 或 2 位停止位;

  • 可编程的奇偶校验 (奇校验和偶校验);

  • 自动波特率检测 (最高支持 115.2Kbit/s);

4.时钟寄存器 - CCM_CSCDR1

UART 的时钟源由寄存器 CCM_CSCDR1 的 UART_CLK_SEL[6] 位选择:

UART_CLK_SEL 为 0 时 UART 的时钟源为 pll3_80m (80MHz)

UART_CLK_SEL 为 1 时 UART 的时钟源为 osc_clk (24M)

一般选择 pll3_80m 作为 UART 的时钟源

寄存器 CCM_CSCDR1 的 UART_CLK_PODF[5:0] 位是 UART 的时钟分频值,可设置 0~63,分别对应 1~64 分频,一般设置为 1 分频,因此最终进入 UART 的时钟频率为 80MHz

5.控制寄存器

UART_UCR1 ~ UART_UCR4 这四个寄存器用于配置 UART 模块的相关功能,如自动波特率使能、奇偶校验模式等

5.1.UART_UCR1

其中,重点关注如下几个位域:

  • ADBR[14]:自动波特率检测使能,为 0 时关闭自动波特率检测,为 1 时使能自动波特率检测;

  • UARTEN[0]:UART 使能,为 0 时关闭 UART,为 1 时使能 UART;

5.2.UART_UCR2

其中,重点关注如下几个位域:

  • IRTS[14]:为 0 时使用 RTS 引脚功能,为 1 时忽略 RTS 引脚;

  • PREN[8]:奇偶校验使能,为 0 时关闭奇偶校验,为 1 时使能奇偶校验;

  • PROE[7]:奇偶校验模式选择,开启奇偶校验后,若该位为 0 则使用偶校验,为 1 使用奇校验;

  • STOP[6]:停止位数量,为 0 表示使用 1 位停止位,为 1 表示使用 2 位停止位;

  • WS[5]:数据位长度,为 0 时选择 7 位数据位,为 1 时选择 8 位数据位;

  • TXEN[2]:发送使能位,为 0 时关闭 UART 的发送功能,为 1 时打开 UART 的发送功能;

  • RXEN[1]:接收使能位,为 0 时关闭 UART 的接收功能,为 1 时打开 UART 的接收功能;

  • SRST[0]:软件复位,向该位写 0 复位 UART,为 1 时表示复位完成,复位完成后此位会自动置 1,表示复位完成,只能向该位写 0,写 1 会被忽略;

5.3.UART_UCR3

这里只关注 RXDMUXSEL[2],该位用于设置复用输入,但是 IMX UART 使用 MUXED 模式,因此该位始终为 1

6.状态寄存器

6.1.UART_USR1

例程未使用该寄存器

6.2.UART_USR2

其中,主要关注一下位域:

  • TXDC[3]:发送完成标志位,为 1 时表明发送缓冲 TxFIFO 和移位寄存器为空,即发送完成,向 TxFIFO 写入数据此位就会自动清零;

  • RDR[0]:数据接收标志位,为 1 时表明至少接收到一个数据,从寄存器 UART_URXD 接收到数据后此位会自动清零;

7.FIFO 控制寄存器 - UART_UFCR

其中,关注 RFDIV[9:7] 位域,其用于设置时钟源的分频系数:

  • 0b000:6 分频;

  • 0b001:5 分频;

  • 0b010:4 分频;

  • 0b011:3 分频;

  • 0b100:2 分频;

  • 0b101:1 分频;

  • 0b110:7 分频;

  • 0b111:保留

这里需要注意的是,分频系数和位域的值不是一一对应的关系

8.波特率寄存器

8.1.分母 - UART_UBIR

BRM(Binary Rate Multiplier)用于生成 16x 的波特率

UART_UBIR 寄存器的 INC[15:0] 位域存储计算波特率的分母:

8.2.分子 - UART_UBMR

UART_UBMR 寄存器的 MOD[15:0] 存储计算波特率的分子:

9.波特率计算

波特率的计算公式如下:

  • Ref Freq:经过分频进入 UART 的时钟频率;

  • UBMR:寄存器 UART_UBMR 中的值,分子;

  • UBIR:寄存器 UART_UBIR 中的值,分母;

通过 UART_UFCR 的 RFDIV 位、UART_UBMR 和 UART_UBIR 寄存器中的值即可计算波特率,例如,要设置 UART 的波特率为 115200,则可以设置 RFDIV 为 0x5 (0b101),即 1 分频,因此 RefFreq = 80MHz,设置 UBIR = 71,UBMR = 3124,根据公式可得:

10.接收数据寄存器 - UART_URXD

其中关注 RX_DATA[7:0] 位域,其中保存接收到的数据,读取该位域即可获得接收的数据,在 7-bit 模式下,最高位强制为 0,在 8-bit 模式下,全部的 8-bit 均为有效数据位

11.发送数据寄存器 - UART_UTXD

TX_DATA[7:0] 保存将要发送的数据,在 7-bit 模式下忽略最高位,在 8-bit 模式下,全部的 8-bit 均为有效位,数据发送时从最低位开始发送,仅当 UART_USR1.TRDY 为 1 时才允许向 TX_DATA[7:0] 写入数据,防止产生冲突

12.代码实现

// uart.h#ifndef _UART_H
#define _UART_H#include "nxp.h"// 函数声明
void uart_init(void);
void uart_io_init(void);
void uart_disable(UART_Type *base);
void uart_enable(UART_Type *base);
void uart_soft_reset(UART_Type *base);
void uart_set_baudrate(UART_Type *base, uint32_t baudrate, uint32_t clk);// 注意,在 stdio.h SDK 中会使用下面这三个接口
// 因此两边的函数定义一定要保持一致
void putc(unsigned char c);
void puts(char *str);
unsigned char getc(void);// 下面这个函数用于防止编译器报错
void raise(int sig_nr);#endif /* _UART_H */
// uart.c#include "uart.h"/* 初始化串口 UART1 使用的 IO 引脚 */
void uart_io_init(void)
{/* 初始化 IO 复用* UART1_RX_DATA 引脚功能选择为 UART1_RXD(配置 IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA 寄存器)* UART1_TX_DATA 引脚功能选择为 UART1_TXD(配置 IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA 寄存器)*/IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX, 0);IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX, 0);/* 配置 UART1_RXD 和 UART1_TXD 引脚的电气特性* 配置 IOMUXC_SW_PAD_CTL_PAD_UART1_TX_DATA 寄存器* 配置 IOMUXC_SW_PAD_CTL_PAD_UART1_RX_DATA 寄存器* 这两个引脚的电气特性一样,相关位域的配置如下:* HYS[16]:使能迟滞比较器,0b0,禁用* PUS[15:14]:上拉/下拉电阻值,0b00,下拉* PUE[13]:引脚状态保持,0b0,不保持引脚电平值* PKE[12]:状态保持器使能,0b1,使能* ODE[11]:开漏输出使能,0b0,禁用* SPEED[7:6]:IO 速率,0b10,100MHz* DSE[5:3]:IO 驱动能力,0b110,R0/6* SRE[0]:压摆率,0b0,低压摆率* 因此,最终要写入寄存器的值为:0x000010B0*/IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX, 0x000010B0);IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_RX, 0x000010B0);
}/* 启用指定的 UART 模块 */
void uart_enable(UART_Type *base)
{/* 向 UART_UCR1 寄存器的 UARTEN[0] 位写 1 使能 UART 模块 */base->UCR1 |= (0b1 << 0);
}/* 禁用指定的 UART 模块 */
void uart_disable(UART_Type *base)
{/* 向 UART_UCR1 寄存器的 UARTEN[0] 位写 0 禁用 UART 模块 */base->UCR1 &= ~(0b1 << 0);
}/* UART 复位 */
void uart_soft_reset(UART_Type *base)
{/* 向 UART_UCR2 寄存器的 SRST[0] 位写 0 发送 UART 模块复位请求* 复位完成后,SRST[0] 会自动恢复为 1*/base->UCR2 &= ~(0b1 << 0);/* 等待复位完成 */while (!(base->UCR2 & 0x00000001)) {}
}/* 设置 UART 的波特率 */
void uart_set_baudrate(UART_Type *base, uint32_t baudrate, uint32_t clk)
{uint32_t numerator = 0; /* 分子 */uint32_t denominator = 0; /* 分母 */uint32_t divisor = 0;uint32_t refFreqDiv = 0;uint32_t divider = 0;uint64_t baudDiff = 0;uint64_t tempNumerator = 0;uint64_t tempDenominator = 0;/* 计算最大的除数 */numerator = clk;denominator = baudrate << 4;divisor = 1;while (denominator != 0){divisor = denominator;denominator = numerator % denominator;numerator = divisor;}numerator = clk / 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 = clk;tempDenominator = (numerator << 4);divisor = 1;/* get the approximately maximum divisor */while (tempDenominator != 0){divisor = tempDenominator;tempDenominator = tempNumerator % tempDenominator;tempNumerator = divisor;}tempNumerator = clk / 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 寄存器base->UBMR = UART_UBMR_MOD(numerator / divider - 1);}
}/* UART1 初始化,波特率 115200 */
void uart_init(void)
{/* 初始化串口 IO 引脚 */uart_io_init();/* 初始化 UART1 */uart_disable(UART1); /* 先禁用 UART1 */uart_soft_reset(UART1); /* 复位 UART1 *//* 清除 UCR1 寄存器 */UART1->UCR1 = 0;/* 关闭自动波特率检测:UART1_UCR1.ADBR[14] 置 0 */UART1->UCR1 &= (0b1 << 14);/* 配置 UART:UART_UCR2 寄存器* IRTS[14]:1,忽略 RTS 引脚* PREN[8]: 0,关闭奇偶校验校验* STPB[6]: 0,1 位停止位* WS[5]:   1,8-bit 数据位* TXEN[2]: 1,使能发送* RXEN[1]: 1,使能接收*/UART1->UCR2 |= (0b1 << 14) | (0b1 << 5) | (0b1 << 2) | (0b1 << 1);/* 配置 UART1_UCR3 寄存器,将 RXDMUXSEL[2] 设置为 1(该位必须始终为 1) */UART1->UCR3 |= (0b1 << 2);/* 设置波特率 */
#if 0/** 波特率计算公式: BaudRate = RefFreq / (16 * (UBMR + 1) / (UBIR+1))* 如果要设置波特率为 115200,则使用如下参数:* RefFreq = 80MHz,寄存器 UART_UFCR 的 RFDIV[9:7] = 0b101, 表示 1 分频* UBMR = 3124* UBIR = 71* 因此,波特率 = 80000000 / (16 * (3124 + 1) / (71 + 1))*            = 80000000 / (16 * 3125 / 72)*            = (80000000 * 72) / (16 * 3125)*            = 115200*/UART1->UFCR = (0b101 << 7); /* refFreq = ipg_clk / 1 = 80MHz */UART1->UBIR = 71;UART1->UBMR = 3124;
#endif/* 也可以通过以下函数计算并设置波特率 */
// #if 0uart_set_baudrate(UART1, 115200, 80000000); /* 设置波特率 */
// #endif/* 串口使能 */uart_enable(UART1);
}/* 通过串口发送一个字符 */
void putc(unsigned char c)
{/* 等待上一次发送完成 */while (!(UART1->USR2 & 0x00000008)) {}/* 发送字符:将字符值(ASCII 码)写入 UTXD 寄存器 */UART1->UTXD = (c & 0xFF);
}/* 发送字符串 */
void puts(char *str)
{char *p = str;while (*p){putc(*p);p++;}
}/* 接收一个字符 */
unsigned char getc(void)
{/* 等待接收完成 */while (!(UART1->USR2 & 0x00000001)) {}/* 返回接收到的字符 */return UART1->URXD;
}/* 该函数仅用于防止编译器报错 */
void raise(int sig_nr) 
{}

相关文章:

  • 数据可视化热图工具:Python实现CSV/XLS导入与EXE打包
  • Python在自动驾驶数据清洗中的应用
  • 路由器实战操作
  • Vue百日学习计划Day36-42天详细计划-Gemini版
  • mysql的安装方式
  • Lambda大数据架构
  • Linux基础开发工具三(git,gdb/cgdb)
  • 为什么wifi有信号却连接不上?
  • password,密码加密解释
  • UE RPG游戏开发练手 第二十九课 重攻技能2
  • Flink 快速入门
  • 从基础到高级:网站反爬技术全景解析与第三方工具对比
  • 2025年- H33-Lc141 --148. 排序链表(快慢指针,快指针先出发一步)--Java版
  • 数据库性能调优:索引设计、缓存配置与查询计划优化
  • SQL练习——(15/81)
  • JavaWeb:SpringBoot处理全局异常(RestControllerAdvice)
  • Pytorch---view()函数
  • 基于不完美维修的定期检测与备件策略联合优化算法matlab仿真
  • 【算法】滑动窗口动态查找不含重复字符的最长子串
  • 算法(最小基因变化+迷宫中离入口最近的出口)
  • 专访《风雪夜归人》导演闫锐:在舞台上表现什么是真正的活着
  • 半年不到再换岗:伊春市委常委、政法委书记方春彪任伊春森工集团党委书记
  • 首届巴塞尔艺术奖公布:大卫·哈蒙斯、曹斐等36人获奖
  • 全总联合六部门印发工作指引,共保劳动者合法权益
  • 东部沿海大省浙江,为何盯上内河航运?
  • 没有握手,采用翻译:俄乌三年来首次直接会谈成效如何?