S32K144 UART通讯

本文介绍了UART工作原理,包括其基本结构、帧格式和通讯过程,还阐述了单片机与PC串口调试通讯时的收发情况。同时,详细说明了S32K44 UART通讯的主要寄存器及三种通讯方式,如使用DATA寄存器普通通讯的代码示例,最后提及FIFO通讯的优化方向。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

目录

一、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 引脚串行输出

  1. CPU 轮询 / 中断判断 TXFIFO 未满,执行代码写入字节到TDR(DATA 发送寄存器)
  2. 硬件自动把 TDR 内数据移入TX_FIFO,立刻释放 TDR,CPU 可继续写下一字节
  3. TX_FIFO 非空时,硬件持续从 FIFO 头部取出 1 字节送入Tx 移位寄存器
  4. 移位寄存器按波特率时钟,逐比特输出起始位 + 数据位 + 校验 + 停止位到 TX 引脚
  5. 单字节发完后,移位寄存器清空;若 TX_FIFO 仍有数据,自动重复步骤 3 继续发送
  6. 触发条件:TX_FIFO 达到空阈值时,置 TXE/TC 标志,通知 CPU 填充更多数据

接收:

RX 引脚采样比特 → Rx 移位寄存器 → RX_FIFO → RDR (DATA) → CPU 读取

  1. RX 引脚检测起始位下降沿,波特率时钟逐位采样电平存入Rx 移位寄存器
  2. 收满完整 1 字节(含校验校验)后,硬件并行送入RX_FIFO,移位寄存器立刻清空,可继续收下一帧
  3. RX_FIFO 持续缓存多字节;当缓存字节达到中断触发阈值(如 1/4 满、半满),置 RXNE 中断标志
  4. CPU 响应中断,循环读取RDR(DATA 接收寄存器),批量取出 FIFO 内所有数据
  5. 全部读取后 RX_FIFO 空位释放,继续缓存后续接收字节

3.2关闭FIFO:

发送:

CPU 写 TDR (DATA) → Tx 移位寄存器 → TX 引脚

  1. CPU 等待TXE=1(TDR 为空),写入数据到 TDR
  2. 硬件瞬间将 TDR 字节载入 Tx 移位寄存器,TDR 变空,TXE 标志置 1
  3. 移位寄存器逐位串行输出;移位寄存器发完前,TDR 不能送入下一字节,否则阻塞、丢帧

接收:

RX 引脚采样比特 → Rx 移位寄存器 → RDR (DATA) → CPU 读取

  1. 移位寄存器收满 1 字节,立刻并行拷贝至 RDR(DATA),置RXNE=1
  2. CPU 必须在下一字节收完前读取 RDR;若未及时读取,新字节会覆盖 RDR,产生数据溢出丢失
  3. 读取 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寄存器,需要注意:在收发使能前设置

These register must be written only when CTRL[RE] and CTRL[TE] are cleared。

中断产生条件:FIFO中接收的字数要大于设置的RXFIFO数才产生中断,RXFIFOSIZE为只读,复位值为1,即FIFO深度芯片固定为4,不可设定,RXWATER为2bit设置,最大设置值为3。当设置为3,发送字节小于3时,就不会产生中断。

When the number of datawords in the receive FIFO/buffer is greater than the value in this register field, an interrupt or a DMA request is generated。
​​​​​​​
  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;
}

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值