目录
一、UART工作原理:
1.帧格式:编辑
2.奇偶校验位:
3.FIFO、DATA 数据寄存器、移位寄存器完整数据流
3.1开启FIFO:
3.2关闭FIFO:
4.通讯过程:
二、S32K44 UART通讯:
1.主要寄存器说明:
BAUD:
CTRL:
STAT:
DATA:
2.三种通讯方式:
2.1.使用DATA寄存器未使用FIFO通讯:
clock.c
uart.c
uart.h
main.c
io.c
2.2.使用DATA寄存器且使用FIFO通讯:
2.3.使用FIFO轮询通讯:
一、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; }