目录
一、UART工作原理:

UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收

⑴输出缓冲寄存器,它接收CPU从数据总线上送来的并行数据,并加以保存。uart基本结构
⑵ 输出移位寄存器,它接收从输出缓冲器送来的并行数据,以发送时钟的速率把数据逐位移出,即将并行数据转换为串行数据输出。
⑶ 输入移位寄存器,它以接收时钟的速率把出现在串行数据输入线上的数据逐位移入,当数据装满后,并行送往输入缓冲寄存器,即将串行数据转换成并行数据。
⑷ 输入缓冲寄存器,它从输入移位寄存器中接收并行数据,然后由CPU取走。
⑸控制寄存器,它接收CPU送来的控制字,由控制字的内容,决定通信时的传输方式以及数据格式等。例如采用异步方式还是同步方式,数据字符的位数,有无奇偶校验,是奇校验还是偶校验,停止位的位数等参数。
⑹状态寄存器。状态寄存器中存放着接口的各种状态信息,例如输出缓冲区是否空,输入字符是否准备好等。在通信过程中,当符合某种状态时,接口中的状态检测逻辑将状态寄存器的相应位置"1",以便让CPU查询。
1.帧格式:
-
起始位:当有数据要发送时,数据信号线变成低电平,并持续一段时间,用于表示字符的开始,这一位称为起始位。
-
数据位:紧接着起始位之后。在信号线上会依次出现待发送的每一位字符,最低有效位D0最先出现,因此它最早被发送出去。采用不同的编码方案,数据位的个数不同,数据位的个数可以是4、5、6、7、8等;当字符用ASCII码表示时,数据位占7位(D0~D7)。
-
奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性,例如数据传输采用奇校验时,如果数据“1”的个数是奇数,这一位就被置为“0”,反之置为“1”。当然系统中也可以不采用奇偶校验。
-
停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。奇偶校验位的后面至少应该有一位高电平表示停止位,如果传输完一个字符后立即传输下一个字符,那么后一个字符的起始位就紧跟着前一个字符的停止位,否则停止位后又进入一个空闲状态。
-
空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。
2.奇偶校验位:
在异步串行通信中,如何知道一个字节的传输是否正确的?最常见的方法是增加一个位(奇偶校验位),供错误检测使用。字符奇偶校验检查(Character Parity Checking,CPC)也称为垂直冗余检查(Vertical Redundancy Checking,VRC),它为每个字节增加一个额外位,使字节中 1 的个数为奇数或偶数,奇数或偶数依据使用的是奇校验检查还是偶校验检查而定。
当使用奇校验检查时,如果字节数据位中 1 的数目是偶数,校验位应为 1,如果 1 的数目是奇数,校验位应为 0;当使用偶校验检查时,如果字符数据位中 1 的数目是偶数,则校验位应为 0,否则为 1。这里列举奇偶校验检查的一个实例,如 ASCII 字符 R,其二进制数为 1010010。由于字符 R 中有 3 个位为 1,若使用奇校验检查,则校验位为 0;若使用偶校验检查,则校验位为 1。
在传输过程中,若有 1 位(或奇数个数据位)发生错误,使用奇偶校验检查可以知道发生传输错误;若有 2 位(或偶数个数据位)发生错误,使用奇偶校验检查就不能知道已经发生了传输错误。但是奇偶校验检查方法简单,使用方便,发生 1 位错误的概率远大于 2 位的概率,所以奇偶校验这种方法还是最为常用的校验方法。
几乎所有 MCU 的串行异步通信接口都提供这种功能。但实际编程使用较少,原因是单字节校验意义不大。
3.FIFO、DATA 数据寄存器、移位寄存器完整数据流

- DATA 寄存器(TDR 发送 / RDR 接收,软件唯一交互入口) CPU 通过 APB 总线读写,只有这个寄存器有内存地址,代码直接操作;发送 / 接收共用同一寄存器地址,硬件内部物理分离。
- 硬件 FIFO(TX_FIFO/RX_FIFO,可选缓存队列) 多字节硬件缓冲区(常见 8/16/32 字节),DATA ↔ 移位寄存器中间缓冲层;无 FIFO 时直接 DATA↔移位寄存器。
- 移位寄存器(Tx Shift Reg / Rx Shift Reg,底层串行转换单元) 无软件地址,硬件私有;负责并行字节 ↔ 串行比特流转换,逐位输出 / 采样 TX/RX 引脚。
3.1开启FIFO:
发送:
CPU 写 TDR (DATA) → TX_FIFO → Tx 移位寄存器 → TX 引脚串行输出
- CPU 轮询 / 中断判断 TXFIFO 未满,执行代码写入字节到TDR(DATA 发送寄存器)
- 硬件自动把 TDR 内数据移入TX_FIFO,立刻释放 TDR,CPU 可继续写下一字节
- TX_FIFO 非空时,硬件持续从 FIFO 头部取出 1 字节送入Tx 移位寄存器
- 移位寄存器按波特率时钟,逐比特输出起始位 + 数据位 + 校验 + 停止位到 TX 引脚
- 单字节发完后,移位寄存器清空;若 TX_FIFO 仍有数据,自动重复步骤 3 继续发送
- 触发条件:TX_FIFO 达到空阈值时,置 TXE/TC 标志,通知 CPU 填充更多数据
接收:
RX 引脚采样比特 → Rx 移位寄存器 → RX_FIFO → RDR (DATA) → CPU 读取
- RX 引脚检测起始位下降沿,波特率时钟逐位采样电平存入Rx 移位寄存器
- 收满完整 1 字节(含校验校验)后,硬件并行送入RX_FIFO,移位寄存器立刻清空,可继续收下一帧
- RX_FIFO 持续缓存多字节;当缓存字节达到中断触发阈值(如 1/4 满、半满),置 RXNE 中断标志
- CPU 响应中断,循环读取RDR(DATA 接收寄存器),批量取出 FIFO 内所有数据
- 全部读取后 RX_FIFO 空位释放,继续缓存后续接收字节
3.2关闭FIFO:
发送:
CPU 写 TDR (DATA) → Tx 移位寄存器 → TX 引脚
- CPU 等待
TXE=1(TDR 为空),写入数据到 TDR - 硬件瞬间将 TDR 字节载入 Tx 移位寄存器,TDR 变空,TXE 标志置 1
- 移位寄存器逐位串行输出;移位寄存器发完前,TDR 不能送入下一字节,否则阻塞、丢帧
接收:
RX 引脚采样比特 → Rx 移位寄存器 → RDR (DATA) → CPU 读取
- 移位寄存器收满 1 字节,立刻并行拷贝至 RDR(DATA),置
RXNE=1 - CPU 必须在下一字节收完前读取 RDR;若未及时读取,新字节会覆盖 RDR,产生数据溢出丢失
- 读取 RDR 硬件自动清零 RXNE 标志,移位寄存器继续接收下一帧
概要:
·移位寄存器永远是最底层,所有数据最终都要经过它才能进出引脚,不受 FIFO 开关影响;
·DATA 寄存器是 CPU 唯一入口,FIFO 只是中间缓冲,没有 FIFO 时 DATA 直接对接移位寄存器;
·无 FIFO 风险:每收发 1 字节就要进一次中断,若接收方接受慢、发送方发送快,外部高速数据流持续从 RX 引脚输入;移位寄存器正在组装新的一字节;此时 RDR(DATA 寄存器)里还存放着上一帧未被 CPU 读取的数据;新字节组装完成后,硬件无处存放新数据,直接触发溢出(Overrun),新字节被丢弃,产生丢数,即高速串口极易丢数据。开启 FIFO 支持批量收发,中断次数大幅减少,开启 FIFO 后会提供 8/16/32 字节硬件缓存: 就算 CPU 临时处理缓慢,新数据可以先存入 FIFO 队列,给 CPU 留出批量读取的缓冲时间,大幅降低溢出丢包概率。
·发送方向:TDR 写完先进 FIFO,再给移位寄存器;接收方向:移位寄存器收完先进 FIFO,CPU 再从 DATA 读 FIFO 数据;
·移位寄存器、FIFO 均无寄存器映射地址,程序员代码中只能操作 DATA 寄存器。
·发送:CPU 写 DATA → FIFO 缓存 → 移位逐位发 TX
·接收:RX 采样进移位 → FIFO 缓存 → CPU 读 DATA 取数据
FIFO 是蓄水池,DATA 是门窗,移位寄存器是搬运比特的底层流水线。
4.通讯过程:
UART发送数据时先发低位后发高位。当总线处于空闲状态时,线路保持高电平。以发送数据0x96为例,发送数据前会先发一个0,让总线从高电平变成低电平,提醒数据接收方做好准备,然后依次从低位到高位发送八位数据。传输完成后,会发一个1,让总线重新回到高电平。

UART数据发送过程中,包含8位数据位,一位起始位,一位停止位。一共10位。如若需要发送新的数据,则重新从起始位开始。
UART属于异步通讯,这意味着没有时钟信号,取而代之的是在数据包中添加开始和停止位。这些位定义了数据包的开始和结束,因此接收UART知道何时读取这些数据。
个人理解:单片机设备和PC串口调试通讯时:
1.单片机发送字符串:发送前,STAT寄存器发送bit状态为空,此时往DATA寄存器上写一个字符,即发送了一个字符,STAT寄存器发送bit状态变为满;此时,PC串口调试助手,马上就收一个字符,STAT寄存器发送bit状态变为空,依次
2.单片机接受字符:接受前,STAT寄存器接受bit为满,从DATA寄存器读取一个字符后,STAT寄存器接受bit变为空。此时,PC串口调试助手检测到STAT发送bit状态为空,马上又发送一个字符,STAT寄存器接受bit变为满,依次
二、S32K44 UART通讯:
备注:各位读者在以下代码有什么问题(比如:时钟为什么要这样设置、引脚为什么这样设置、波特率这样计算等)都可以留言提出来,我会一一解答,因为我在自己摸索过程中也踩雷不少,希望借该文章对同行尽力所能及的帮助。
1.主要寄存器说明:

BAUD:
备注:UART 是异步串行通信,收发双方没有共享时钟线,完全依靠约定相同波特率来同步每一位的采样时刻: 接收端按固定时间间隔(波特率倒数)对 RX 引脚电平采样; 一旦波特率不同,采样点会持续偏移,最终解析不出正确字节。


配置生成波特率
CTRL:



是能串口功能
STAT:


判断通讯是否可以进行
DATA:

数据收发
2.三种通讯方式:
2.1.使用DATA寄存器未使用FIFO通讯:
使用中断方式接受数据,直接往DATA写数据进行发送。源码如下,读者可以直接复制使用:
clock.c
#include "S32K144.h"
void SOSC_init_8MHz(void) {
SCG->SOSCDIV=0x00000101; /* SOSCDIV1 & SOSCDIV2 =1: divide by 1 */
SCG->SOSCCFG=0x00000024; /* Range=2: Medium freq (SOSC betw 1MHz-8MHz)*/
/* HGO=0: Config xtal osc for low power */
/* EREFS=1: Input is external XTAL */
while(SCG->SOSCCSR & SCG_SOSCCSR_LK_MASK); /* Ensure SOSCCSR unlocked */
SCG->SOSCCSR=0x00000001; /* LK=0: SOSCCSR can be written */
/* SOSCCMRE=0: OSC CLK monitor IRQ if enabled */
/* SOSCCM=0: OSC CLK monitor disabled */
/* SOSCERCLKEN=0: Sys OSC 3V ERCLK output clk disabled */
/* SOSCLPEN=0: Sys OSC disabled in VLP modes */
/* SOSCSTEN=0: Sys OSC disabled in Stop modes */
/* SOSCEN=1: Enable oscillator */
while(!(SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK)); /* Wait for sys OSC clk valid */
}
void SPLL_init_160MHz(void) {
while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK); /* Ensure SPLLCSR unlocked */
SCG->SPLLCSR = 0x00000000; /* SPLLEN=0: SPLL is disabled (default) */
SCG->SPLLDIV = 0x00000302; /* SPLLDIV1 divide by 2; SPLLDIV2 divide by 4 */
SCG->SPLLCFG = 0x00180000; /* PREDIV=0: Divide SOSC_CLK by 0+1=1 */
/* MULT=24: Multiply sys pll by 16 + 24=40 */
/* SPLL_CLK = 8MHz / 1 * 40 / 2 = 160 MHz */
while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK); /* Ensure SPLLCSR unlocked */
SCG->SPLLCSR = 0x00000001; /* LK=0: SPLLCSR can be written */
/* SPLLCMRE=0: SPLL CLK monitor IRQ if enabled */
/* SPLLCM=0: SPLL CLK monitor disabled */
/* SPLLSTEN=0: SPLL disabled in Stop modes */
/* SPLLEN=1: Enable SPLL */
while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK)); /* Wait for SPLL valid */
}
void NormalRUNmode_80MHz (void) { /* Change to normal RUN mode with 8MHz SOSC, 80 MHz PLL*/
SCG->RCCR=SCG_RCCR_SCS(6) /* PLL as clock source*/
|SCG_RCCR_DIVCORE(0b01) /* DIVCORE=1, div. by 2: Core clock = 160/2 MHz = 80 MHz*/
|SCG_RCCR_DIVBUS(0b01) /* DIVBUS=1, div. by 2: bus clock = 40 MHz*/
|SCG_RCCR_DIVSLOW(0b10); /* DIVSLOW=2, div. by 2: SCG slow, flash clock= 26 2/3 MHz*/
if ((SCG->CSR & SCG_CSR_SCS_MASK >> SCG_CSR_SCS_SHIFT ) != 6) {}
/* Wait for sys clk src = SPLL */
}
void init_clks_80MHz(void)
{
// SMC->PMPROT = SMC_PMPROT_AVLP_MASK; //allow VLPS mode
// SMC->STOPCTRL = SMC_STOPCTRL_STOPO(1); //1:BOTH system and bus clock disable;2:system clock disable, bus clock enabled
// SMC->PMCTRL = 0x00000002;//+ SMC_PMCTRL_RUNM_MASK; //select very-low-power stop
// while (!(SMC->PMSTAT & 1)){ ; } //wait RUN ok.
SOSC_init_8MHz();
SPLL_init_160MHz();
NormalRUNmode_80MHz();
}
uart.c
/*
* LPUART.c Copyright NXP 2016
* Description: LPUART functions
* 2015 Sept 28 Kushal Shaw - original version AN5213;
* 2016 Mar 17 O Romero - ported to S32K144;
* 2017 Jul 03 SM: Correced "receive" spelling for function
*/
#include "S32K144.h" /* include peripheral declarations S32K144 */
#include "uart.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
UART_DATA_t uart_data;
/*
数据位:8bit 优化:其他bit数据通讯7、9、10
停止位:1bit 优化:2bit停止位
校验位:disable 优化:开启奇偶检验
数据通讯方式:UART模块DATA寄存器 优化:1.DMA;2.FIFO 轮询;3.FIFO中断1字节或者N字节中断
实验:
1.LPUART_s32K144工程循环收1个字节,看是否能接收字符串
2.在lpuart_echo_s32k144工程上改变接收长度1->N看是否能接收字符串
3.在lpuart_echo_s32k144工程上改变数据位、停止位、校验位通讯
4.优化当前代码,通过参数输入选择数据位、停止位、校验位、通讯方式进行正常通讯
*/
void set_baud(void)
{
// look for best suitable BAUD
U32 osr = 4, tempDiff, calculatedBaud, baudDiff;
U16 sbr, sbrTemp, i;
// First calculate the baud rate using the minimum OSR possible
sbr = (uint16_t)(8000000 / (115200 * osr));
calculatedBaud = (8000000 / (osr * sbr));
if (calculatedBaud > 115200)
{
baudDiff = calculatedBaud - 115200;
}
else
{
baudDiff = 115200 - calculatedBaud;
}
/* loop to find the best osr value possible, one that generates minimum baudDiff
* iterate through the rest of the supported values of osr */
for (i = 5U; i <= 32U; i++)
{
/* calculate the temporary sbr value */
sbrTemp = (uint16_t)(8000000 / (115200 * i));
/* calculate the baud rate LPUART1d on the temporary osr and sbr values */
calculatedBaud = (8000000 / (i * sbrTemp));
if (calculatedBaud > 115200)
{
tempDiff = calculatedBaud - 115200;
}
else
{
tempDiff = 115200 - calculatedBaud;
}
if (tempDiff <= baudDiff)
{
baudDiff = tempDiff;
osr = i; /* update and store the best osr value calculated */
sbr = sbrTemp; /* update store the best sbr value calculated */
}
}
/* Check if osr is between 4x and 7x oversampling.
* If so, then "BOTHEDGE" sampling must be turned on */
if ((osr > 3U) && (osr < 8U))
{
LPUART1->BAUD |= (LPUART1->BAUD & ~LPUART_BAUD_BOTHEDGE_MASK) | (1 << LPUART_BAUD_BOTHEDGE_SHIFT);
}
/* program the osr value (bit value is one less than actual value) */
// LPUART1->BAUD &= ~(LPUART_BAUD_OSR_MASK);
tempDiff = LPUART1->BAUD;
tempDiff &= ~(LPUART_BAUD_OSR_MASK);
tempDiff |= LPUART_BAUD_OSR(osr - 1);
LPUART1->BAUD = tempDiff;
// LPUART1->BAUD &= ~(0x1f << 24);
// LPUART1->BAUD |= (osr - 1) << 24;
/* write the sbr value to the BAUD registers */
LPUART1->BAUD &= ~(LPUART_BAUD_SBR_MASK);
LPUART1->BAUD |= sbr << 0;
}
void LPUART1_init(void) /* Init. summary: 9600 baud, 1 stop bit, 8 bit format, no parity */
{
PCC->PCCn[PCC_LPUART1_INDEX] &= ~PCC_PCCn_CGC_MASK; /* Ensure clk disabled for config */
PCC->PCCn[PCC_LPUART1_INDEX] |= PCC_PCCn_PCS(0b001) /* Clock Src= 1 (SOSCDIV2_CLK) */
| PCC_PCCn_CGC_MASK; /* Enable clock for LPUART1 regs */
set_baud();
//LPUART1->BAUD = 0x0F000034; /* Initialize for 9600 baud, 1 stop: */
/* SBR=52 (0x34): baud divisor = 8M/9600/16 = ~52 */
/* OSR=15: Over sampling ratio = 15+1=16 */
/* SBNS=0: One stop bit */
/* BOTHEDGE=0: receiver samples only on rising edge */
/* M10=0: Rx and Tx use 7 to 9 bit data characters */
/* RESYNCDIS=0: Resync during rec'd data word supported */
/* LBKDIE, RXEDGIE=0: interrupts disable */
/* TDMAE, RDMAE, TDMAE=0: DMA requests disabled */
/* MAEN1, MAEN2, MATCFG=0: Match disabled */
LPUART1->CTRL=0x000C0000; /* Enable transmitter & receiver, no parity, 8 bit char: */
/* RE=1: Receiver enabled */
/* TE=1: Transmitter enabled */
/* PE,PT=0: No hw parity generation or checking */
/* M7,M,R8T9,R9T8=0: 8-bit data characters*/
/* DOZEEN=0: LPUART enabled in Doze mode */
/* ORIE,NEIE,FEIE,PEIE,TIE,TCIE,RIE,ILIE,MA1IE,MA2IE=0: no IRQ*/
/* TxDIR=0: TxD pin is input if in single-wire mode */
/* TXINV=0: TRansmit data not inverted */
/* RWU,WAKE=0: normal operation; rcvr not in statndby */
/* IDLCFG=0: one idle character */
/* ILT=0: Idle char bit count starts after start bit */
/* SBK=0: Normal transmitter operation - no break char */
/* LOOPS,RSRC=0: no loop back */
// Enable LPUART interrupt.
S32_NVIC->ISER[(uint32_t)(LPUART1_RxTx_IRQn) >> 5U] = (uint32_t)(1UL << ((uint32_t)(LPUART1_RxTx_IRQn) & (uint32_t)0x1FU));
}
void LPUART1_transmit_char(char send) { /* Function to Transmit single Char */
while((LPUART1->STAT & LPUART_STAT_TDRE_MASK)>>LPUART_STAT_TDRE_SHIFT==0);
/* Wait for transmit buffer to be empty */
LPUART1->DATA=send; /* Send data */
}
void LPUART1_transmit_string(char data_string[]) { /* Function to Transmit whole string */
uint32_t i=0;
while(data_string[i] != '\0') { /* Send chars one at a time */
LPUART1_transmit_char(data_string[i]);
i++;
}
}
char LPUART1_receive_char(void) { /* Function to Receive single Char */
char recieve;
while((LPUART1->STAT & LPUART_STAT_RDRF_MASK)>>LPUART_STAT_RDRF_SHIFT==0);
/* Wait for received buffer to be full */
recieve= LPUART1->DATA; /* Read received data*/
return recieve;
}
void LPUART1_receive_and_echo_char(void) { /* Function to echo received char back */
char send[RECV_LEN + 2] = {0};
char buf[RECV_LEN + 2] = {0};
short i = 0;
if ((LPUART1->STAT >> LPUART_STAT_RDRF_SHIFT) || (LPUART1->CTRL >> LPUART_CTRL_RIE_SHIFT)) {
// err
buf[i++] = LPUART1->DATA; // 读数据清RDRF,兜底
LPUART1_transmit_string(buf);
}
for (i = 0; i < RECV_LEN; i++) {
buf[i] = 'a';
}
uart_data.rxBuff = send;
uart_data.rxSize = RECV_LEN + 2; // 2用于存放\r\n两字符
LOG("start recv! recvlen:%d, out:0x%x, str:%s\n", 1048574, 0x12345678, buf);
/* Enable receive data full interrupt */
LPUART1->CTRL |= 1 << LPUART_CTRL_RIE_SHIFT;
while (uart_data.rxSize);
if (!strncmp(send, buf, strlen(buf))) {
LPUART1_transmit_string("罗紫屹努力考701\r\n");
}
}
void LPUART1_RxTx_IRQHandler(void)
{
UART_DATA_t *data_type = &uart_data;
/* Handle receive data full interrupt */
if (LPUART1->CTRL >> LPUART_CTRL_RIE_SHIFT)
{
if (LPUART1->STAT >> LPUART_STAT_RDRF_SHIFT)
{
/* Get data and put in receive buffer */
// 8 bit per char, 9 and 10bit ??? TODO:
*data_type->rxBuff = (U8)LPUART1->DATA;
/* Update the internal state */
++data_type->rxBuff;
--data_type->rxSize;
/* Finish reception if this was the last byte received */
if (data_type->rxSize == 0U)
{
/* Complete transfer, will disable rx interrupt */
LPUART1->CTRL &= ~(1 << LPUART_CTRL_RIE_SHIFT) ;
}
}
}
}
void DecToChar(U8 *num)
{
assert(num);
U8 HexChar[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
if (*num == 0) {
*num = HexChar[0];
} else if (*num == 1) {
*num = HexChar[1];
} else if (*num == 2) {
*num = HexChar[2];
} else if (*num == 3) {
*num = HexChar[3];
} else if (*num == 4) {
*num = HexChar[4];
} else if (*num == 5) {
*num = HexChar[5];
} else if (*num == 6) {
*num = HexChar[6];
} else if (*num == 7) {
*num = HexChar[7];
} else if (*num == 8) {
*num = HexChar[8];
} else if (*num == 9) {
*num = HexChar[9];
} else if (*num == 10) {
*num = HexChar[10];
} else if (*num == 11) {
*num = HexChar[11];
} else if (*num == 12) {
*num = HexChar[12];
} else if (*num == 13) {
*num = HexChar[13];
} else if (*num == 14) {
*num = HexChar[14];
} else if (*num == 15) {
*num = HexChar[15];
}
}
void DecToHexStr(U32 DecNum, U8 *dest, U16 *index)
{
U32 HexI = 1;
U8 HexP = 0;
U8 str[9] = {0};
U8 i = 0;
U8 len;
while (HexI > 0) {
HexI = DecNum / 16;
HexP = DecNum % 16;
DecNum = HexI;
DecToChar(&HexP);
str[i++] = HexP;
}
len = strlen(str);
for (i = 0; str[i] != 0; i++) {
*dest++ = str[len - 1 - i];
*index += 1;
}
}
void self_printf(const U8* format, ...)
{
va_list ap;
U16 i = 0;
U8 buf[1024] = {0};
va_start(ap, format);
while (*format != 0) {
if (*format == '%') {
if (*(format + 1) == 'c') {
buf[i++] = va_arg(ap, char);
} else if (*(format + 1) == 'd') {
sprintf(&buf[i], "%u", va_arg(ap, U32));
i += strlen(&buf[i]);
} else if (*(format + 1) == 'x') {
// sprintf(&buf[i], "%x", va_arg(ap, U32));
// i += strlen(&buf[i]);
DecToHexStr(va_arg(ap, int), &buf[i], &i);
} else if (*(format + 1) == 's') {
strcpy(&buf[i], (char *)va_arg(ap, U32));
i += strlen(&buf[i]);
} else if (*(format + 1) == 'p') {
}
format += 2;
} else {
buf[i++] = *format++;
}
}
buf[i] = 0;
LPUART1_transmit_string(buf);
}
uart.h
/*
* LPUART.h
*
* Created on: Mar 17, 2016
* Author: B46911
*/
#ifndef LPUART_H_
#define LPUART_H_
#include "common.h"
typedef struct uart_data_type {
char *rxBuff;
U32 rxSize;
char *txBuff;
U32 txSize;
} UART_DATA_t;
/*
* 1.DMA;
* 2.非FIFO轮询;
* 3.非FIFO中断;
* 4.FIFO 轮询;
* 5.FIFO中断1字节或者N字节中断
*/
enum communicate {
E_DMA,
E_NO_FIFO_POLL,
E_NO_FIFO_IRQ, // 中断接受时,应用层不知道接受的数据有多长,接受数据长度固定,此时每接受一个字节(8bit传输)会产生一次中断,若没有中断认为数据接受完
E_FIFO_POLL,
E_FIFO_IRQ
};
#define COMMUNICATE_MODE 2
#define RECV_LEN 258
void LPUART1_init(void);
void LPUART1_transmit_char(char send);
void LPUART1_transmit_string(char data_string[]);
char LPUART1_receive_char(void);
void LPUART1_receive_and_echo_char(void);
#define LOG(format, ...) self_printf(format, ##__VA_ARGS__);
#define LOG1(...) self_printf(__VA_ARGS__);
#endif /* LPUART_H_ */
main.c
#include "clock.h"
#include "can.h"
#include "io.h"
#include "systick.h"
#include "wdog.h"
#include "timer.h"
#include "common.h"
#include "adc.h"
#include "get_various_clock.h"
#include "uart.h"
#include "rtc.h"
int main()
{
// U32 adc_value;
// U8 buf[8];
// U8 can_buf[8] = { 0 };
// U8 i = 0;
init_clks_80MHz(); // 研究寄存器
port_init();
// FLEXCAN0_init(); // 研究寄存器
LPUART1_init();
// RTC_Init();
// systick_init(80000000);
// TimerInt();
// wdog_init();
// adc_init();
// flash_init();
while (1)
{
//led_funtion();
// if (0x777 == FLEXCAN0_receive_msg(can_buf))
// {
// FLEXCAN0_transmit_msg("12345678", 0x123);
// }
// if (i == 8) {
// i = 0;
// }
LPUART1_receive_and_echo_char(); // wdg and uart module 相互影响
//
//wdtExtReset();
//read_adc_value(&adc_value);
// datas[0]++;
// FLASH_DRV_EraseSector(&flashSSDConfig, 0x31000, 4096);
// FLASH_DRV_Program(&flashSSDConfig, 0x31000, 32, datas);
//wdog_feed();
}
return 0;
}
io.c
/*
* io.c
*
* Created on: 2022年7月15日
* Author: Administrator
*/
#include "io.h"
#include "systick.h"
#include "timer.h"
#include "common.h"
#include "S32K144.h"
/*************************************
* 输入参数:NO
* 输出参数:NO
* 函数功能:芯片IO口初始化,包括按键1中断初始化
* 返回值:NO
*************************************/
void port_init(void)
{
unsigned char i;
// PTA
// enble PTA端口clock
PCC->PCCn[PCC_PORTA_INDEX] |= PCC_PCCn_CGC_MASK; // Enable clock for PORTA
// 配置PORTA之 A0/A1/A2/A3为 GPIO输出,分别对应开发板上的4个小灯LED1/LED2/LED3/LED4
// 管脚复用为GPIO
for (i = 0; i < 4; i++)
{
PTA->PDDR |= 1 << i; // Port An: Data Direction = output
PORTA->PCR[i] = 0x100; // Port A0: MUX = ALT1, GPIO
}
// PTB
//PCC->PCCn[PCC_PORTB_INDEX] |= PCC_PCCn_CGC_MASK; // Enable clock for PORTB
//PTC
// PTC10 key1
PCC->PCCn[PCC_PORTC_INDEX] |= PCC_PCCn_CGC_MASK; // Enable clock for PORTC
PTC->PDDR &= ~(1 << 10); // Port An: Data Direction = input
PORTC->PCR[10] = 0xa0100; // Port A0: MUX = ALT1, GPIO // interrupt on falling-edge
// UART
PORTC->PCR[6]|=PORT_PCR_MUX(2); /* Port C6: MUX = ALT2,UART1 TX */
PORTC->PCR[7]|=PORT_PCR_MUX(2); /* Port C7: MUX = ALT2,UART1 RX */
// 外部中断配置
S32_NVIC->ICPR[1] = 1 << (PORTC_IRQn % 32);
S32_NVIC->ISER[(uint32_t)(PORTC_IRQn) >> 5U] = (uint32_t)(1UL << ((uint32_t)(PORTC_IRQn) & (uint32_t)0x1FU));
// PTD
PCC->PCCn[PCC_PORTD_INDEX] |= PCC_PCCn_CGC_MASK; // Enable clock for PORTD
// PTD13, RTC_CLKOUT
PORTD->PCR[13] &= ~PORT_PCR_MUX_MASK;
PORTD->PCR[13] |= PORT_PCR_MUX(7);
//PTE
/* CAN RX TX PTE4 PTE5*/
PCC->PCCn[PCC_PORTE_INDEX] |= PCC_PCCn_CGC_MASK; /* Enable clock for PORTE */
PORTE->PCR[4] |= PORT_PCR_MUX(5); /* Port E4: MUX = ALT5, CAN0_RX */
PORTE->PCR[5] |= PORT_PCR_MUX(5); /* Port E5: MUX = ALT5, CAN0_TX */
}
void PORTC_IRQHandler()
{
U8 i;
U8 j = 2;
PTA->PDOR = 15;
while (j)
{
for (i = 0; i < 4; i++)
{
PTA->PDOR &= ~(1 << i);
timer_delay();
// 这里不能使用systick_delay_times做延时,系统滴答靠滴答中断计数,此时还未退出按键中断
PTA->PDOR |= 1 << i;
timer_delay();
}
j--;
}
PORTC->ISFR |= 1 << 10; //清除中断标志位
}
void led_funtion(void)
{
uint8_t i;
PTA->PDOR = 15;
for (i = 0; i < 4; i++)
{
PTA->PDOR &= ~(1 << i);
systick_delay_times(100);
PTA->PDOR |= 1 << i;
systick_delay_times(100);
}
}
2.2.使用DATA寄存器且使用FIFO通讯:
使用中断带有FIFO的方式收、发数据。源码如下,读者可以直接复制使用:
额外寄存器配置:
FIFO:


WATER:


代码初始化时配置FIFO和WATER寄存器,需要注意:在收发使能前设置
中断产生条件:FIFO中接收的字数要大于设置的RXFIFO数才产生中断,RXFIFOSIZE为只读,复位值为1,即FIFO深度芯片固定为4,不可设定,RXWATER为2bit设置,最大设置值为3。当设置为3,发送字节小于3时,就不会产生中断。
LPUART1->CTRL &= ~((1 << 18) | (1 << 19));
LPUART1->FIFO |= ((1 << 7) | (1 << 3));
LPUART1->WATER |= (3 << 16);
2.3.使用FIFO轮询通讯:
轮询:就是周期性去操作DATA寄存器。
这里说明一下数据位非8bit通讯方式,如:9bit,10bit
CTRL:

9bit/10bit数据需要2字节,前8bit直接操作DATA寄存器,剩下的1/2bit有CTRL寄存器操作
void LPUART_Putchar10(LPUART_Type * base, uint16_t data)
{
uint8_t ninthDataBit, tenthDataBit;
uint32_t ctrlRegVal;
volatile uint8_t * dataRegBytes = (volatile uint8_t *)(&(base->DATA));
ninthDataBit = (uint8_t)((data >> 8U) & 0x1U);
tenthDataBit = (uint8_t)((data >> 9U) & 0x1U);
/* write to ninth/tenth data bit (T[0:7]=8-bits, T8=9th bit, T9=10th bit) */
ctrlRegVal = base->CTRL;
ctrlRegVal = (ctrlRegVal & ~LPUART_CTRL_R9T8_MASK) | ((uint32_t)ninthDataBit << LPUART_CTRL_R9T8_SHIFT);
ctrlRegVal = (ctrlRegVal & ~LPUART_CTRL_R8T9_MASK) | ((uint32_t)tenthDataBit << LPUART_CTRL_R8T9_SHIFT);
base->CTRL = ctrlRegVal;
/* write to 8-bits to the data register */
dataRegBytes[0] = (uint8_t)data;
}
本文介绍了UART工作原理,包括其基本结构、帧格式和通讯过程,还阐述了单片机与PC串口调试通讯时的收发情况。同时,详细说明了S32K44 UART通讯的主要寄存器及三种通讯方式,如使用DATA寄存器普通通讯的代码示例,最后提及FIFO通讯的优化方向。
7351

被折叠的 条评论
为什么被折叠?



