MPC5744P (八) SPI
2026/8/30 6:26:09 网站建设 项目流程
代码工程上传了,大家自取:
https://pan.baidu.com/s/11aew6hw3XaAaoaPlzu7XYw?pwd=6666
串行外设接口(SPI)模块提供同步串行总线,用于芯片与外部外设器件之间的通信。
在本章节中简单的介绍了如何使用SPI与外设通信。

(一)SPI特性

本模块支持以下功能:
  • 全双工、三线同步传输
  • 主机模式
  • 从机模式
  • 从机模式下,支持从选信号持续有效时的数据流传输
  • 发送缓冲:采用深度为 5 级的发送先入先出缓存(TX FIFO)实现发送缓冲操作
  • 接收缓冲:采用深度为 5 级的接收先入先出缓存(RX FIFO)实现接收缓冲操作
  • TX FIFO、RX FIFO 可独立关闭,实现 SPI 队列的低延迟更新
  • 支持查看 TX、RX FIFO 状态,便于调试
  • 可按帧配置传输属性:
    • 4 组传输属性寄存器
    • 串行时钟(SCK)极性、相位可编程配置
    • 多种可编程延时
    • 串行帧长度可编程:4 ~ 16 位
      • 超过 16 位的 SPI 帧可通过持续片选格式实现传输
    • 支持片选信号持续保持功能
  • 8 路外设片选信号(PCSE),外接多路分配器可扩展至 256 路
  • 外接多路分配器时,最多支持 128 路外设片选(PCSE)的信号消抖功能
  • DMA 支持:向 TX FIFO 写入数据、从 RX FIFO 读取数据
    • TX FIFO 非满(TFFF)
    • RX FIFO 非空(RFDF)
  • 中断触发条件:
    • 队列传输完成(EOQF)
    • TX FIFO 非满(TFFF)
    • 当前帧传输完成(TCF)
    • 发送 FIFO 为空时尝试发送(TFUF)
    • RX FIFO 非空(RFDF)
    • 接收 FIFO 已满时收到新帧(RFOF)
  • 全局中断请求线
  • 改良型 SPI 传输格式,可适配低速外设器件通信
  • 低功耗架构特性:
    • 支持停机模式
MPC5744P的SPI模块的框图如下所示:

(二)寄存器

SPI模块的所有寄存器如下图所示。

(1)SPI_MCR

模块配置寄存器,该寄存器包含多位配置字段,用于设置模块运行相关的各类工作属性。HALT 位与 MDIS 位可随时改写,但配置生效时机为下一个帧边界。当模块处于运行状态时,仅允许修改 MCR 寄存器中的 HALT 和 MDIS 位。

(2)SPI_CTARn

时钟与传输属性寄存器用于定义各类传输属性。模块处于运行状态时,禁止对 CTAR 寄存器执行写操作
主机模式下,CTAR 寄存器可配置一组传输属性,包括帧长度、时钟相位与极性、数据位传输顺序、波特率以及各类延时参数。从机模式下,仅使用 CTAR0 中的部分位域配置从机传输属性。
模块配置为 SPI 主机时,TX FIFO 表项命令段内的 CTAS 字段,用于选择本次传输使用哪一路 CTAR 寄存器配置;模块配置为 SPI 总线从机时,固定使用 CTAR0 寄存器。
(注:当使用一路SPI控制不通时钟与传输属性的外设时,就可以用到该寄存器。比如SPI0要与W25Q64(1MHz的时钟、CPHA=0、CPOL=0)和DRV8343(2MHz的时钟、CPHA=1、CPOL=0)通信,就可以将CTAR0配置为1MHz的时钟、CPHA=0、CPOL=0,将CTAR1配置为2MHz的时钟、CPHA=1、CPOL=0,在传输时在SPI_PUSHR寄存器中选择要使用CTAR0还是CTAR1即可)
该寄存器内主要涉及到SCK波特率配置极性和相位配置、传输延迟配置(目前我还没用到,有需求详见手册)
1.SCK波特率配置
在手册中提到的SCK波特率计算方法如下图所示:
$$SCK波特率= (fP /PBR)*[(1+DBR)/BR]$$
式中fP为外设时钟频率,在本工程中已配置为 fP = 40MHz(参考本系列教程的(三)系统时钟的PBRIDGEx_CLK),PBR默认值为2,DBR默认值为0,BR根据自己需求设置。
2.极性和相位配置
主机模式下,时钟与传输属性寄存器(CTARn)中的 CPOL、CPHA 位用于配置串行时钟 SCK 的极性与相位:
  • CPOL:配置 SCK 空闲状态的电平极性
  • CPHA:配置 SOUT 上的数据是在第一个 SCK 边沿之前有效,还是在第一个 SCK 边沿时刻有效
当CPHA=0时:
当CPHA=1时:
寄存器详解如下:

(3)SPI_SR

状态寄存器(SR)包含状态位与标志位。这些位用于反映外设模块运行状态,并标记可触发中断或 DMA 请求的事件是否发生。软件向 SR 中的标志位写入 1,即可清除该标志位;向标志位写入 0 则无效。受省电机制影响,模块禁用模式下该寄存器可能不可写。

(4)SPI_RSER

RSER 用于控制 DMA 请求与中断请求;模块处于运行状态时,禁止对 RSER 寄存器执行写操作

(5)SPI_PUSHR

该寄存器用于指定待送入发送 FIFO 的数据。对该寄存器执行 8 位或 16 位写访问时,都会将完整 32 位数据送入发送 FIFO。在主机模式下,该寄存器承载 16 位传输数据与 16 位命令信息。读取 PUSHR 寄存器,将返回发送 FIFO 队首的数据条目。
当外设模块处于禁用状态时,向该寄存器写入数据不会更新 FIFO 内容。因此,模块禁用期间执行读操作,读取到的是模块启用状态下最后一次写入 PUSHR 寄存器的值。

(6)SPI_POPR

POPR 用于读取接收 FIFO。对 POPR 执行 8 位、16 位读操作,和 32 位读操作对接收 FIFO 产生的作用完全一致。向该寄存器执行写操作,会触发传输错误

(三)功能

先搁置一下,过两天补。。。最近太忙了

(四)代码

main.c文件
#include "derivative.h" /* include peripheral declarations */ #include "project.h" #include "mode_entry.h" #include "gpio.h" #include "uart.h" #include "spi.h" extern void xcptn_xmpl(void); void peri_clock_gating(void); /* Configure gating/enabling peri. clocks */ __attribute__ ((section(".text"))) int main(void) { int counter = 0; xcptn_xmpl (); /* Configure and Enable Interrupts */ peri_clock_gating(); /* Configure gating/enabling peri. clocks for modes*/ system160mhz(); /* sysclk=160MHz, dividers configured, mode trans*/ gpio_output(PA5,SIUL2_SRC_FULL_DIS,SIUL2_SSS_1); //PA5 SPI1_CS0 gpio_output(PA6,SIUL2_SRC_FULL_DIS,SIUL2_SSS_1); //PA6 SPI1_SCK gpio_output(PA7,SIUL2_SRC_FULL_DIS,SIUL2_SSS_1); //PA7 SPI1_MOSI gpio_input(PA8,SIUL2_NULL,44,SIUL2_SSS_1); //PA8 SPI1_MISO spi_init(SPI1,SPI_PCS0,SPI_POL_LOW,SPI_PHA_1,SPI_BR_16); uint8_t RecDataMaster[4]; uint8_t SenDataMaster[4]; uint16_t time1,time2; SenDataMaster[0] = 0x9F; SenDataMaster[1] = 0xFF; SenDataMaster[2] = 0xFF; SenDataMaster[3] = 0xFF; for(;;) { spi_swap_string(SPI1,&SenDataMaster,&RecDataMaster,4); counter++; } return 0; } void peri_clock_gating (void) { MC_ME.RUN_PC[0].R = 0x00000000; /* gate off clock for all RUN modes */ MC_ME.RUN_PC[1].R = 0x000000FE; /* enable peri clock for all RUN modes */ /* Note: RUN_PC0 is default cfg for PCTLs */ MC_ME.PCTL204.B.RUN_CFG = 0x00000001; //开启LIN_0时钟门 MC_ME.PCTL91.B.RUN_CFG = 0x00000001; //开启LIN_1时钟门 MC_ME.PCTL98.B.RUN_CFG = 0x00000001; //开启SPI_1时钟门 }

spi.c文件

/** * @file spi.c * @brief spi驱动 * @details 适配MPC5744P * @author loopy * @date 2026-08-16 * @version V1.0 * @note 无 */ //头文件包含 #include "spi.h" #include "project.h" volatile struct SPI_tag *SPI[SPI_MAX] = {&SPI_0, &SPI_1, &SPI_2, &SPI_3}; /** * @brief spi初始化函数 * @param spi_num: SPI模块编号 * @param spi_pcs: 启用的片选引脚 * @param spi_pol: SCK的极性 * @param spi_pha: 相位 * @param spi_br: 分频系数 * @return 无 * @note 无 */ void spi_init(spi_num spi_num,uint8_t spi_pcs,spi_pol spi_pol,spi_pha spi_pha,uint8_t spi_br) { SPI[spi_num]->MCR.B.MSTR = 1; //使能主机模式 SPI[spi_num]->MCR.B.PCSIS = spi_pcs; //使能片选引脚 SPI[spi_num]->MCR.B.MDIS = 0; //SPI[spi_num]->MCR.B.MTFE = 1; //SPI[spi_num]->MCR.B.SMPL_PT = 0; SPI[spi_num]->MCR.B.HALT = 1; //停止模式下配置 SPI[spi_num]->MODE.CTAR[0].B.CPOL = spi_pol; SPI[spi_num]->MODE.CTAR[0].B.CPHA = spi_pha; SPI[spi_num]->MODE.CTAR[0].B.FMSZ = 7; //8字节传输 /* 时钟计算SCK baud rate = (fP /PBR) x [(1+DBR)/BR] * fP为外设时钟频率,在本工程中已配置为 fP = 40MHz * PBR默认值为2,DBR默认值为0,BR根据自己需求设置 */ SPI[spi_num]->MODE.CTAR[0].B.BR = spi_br; //分频系数 SPI[spi_num]->MCR.B.HALT = 0; //停止模式下配置 } /** * @brief spi发送接收字节函数 * @param spi_num: SPI模块编号 * @param byte: 传输的byte * @param spi_keepcs: 启用的片选引脚 * @return recdata: 读取返回值 * @note 当前默认片选脚0 */ uint8_t spi_swap_byte(spi_num spi_num,uint8_t byte,spi_keepcs spi_keepcs) { uint8_t recdata = 0; SPI[spi_num]->PUSHR.PUSHR.R = 0x08010000 | byte | (uint32_t)((uint32_t)spi_keepcs<<31); while (SPI[spi_num]->SR.B.TCF != 1){} while (SPI[spi_num]->SR.B.RFDF != 1){} recdata = SPI[spi_num]->POPR.R; SPI[spi_num]->SR.R = 0xDA0A0000; return recdata; } /** * @brief spi发送接收字符串函数 * @param spi_num: SPI模块编号 * @param *send_data: 发送的数组 * @param *read_data: 接收的数组 * @param len: 数组长度 * @return 无 * @note 无 */ void spi_swap_string(spi_num spi_num,uint8_t *send_data,uint8_t *read_data,uint8_t len) { uint8_t i = 0; for(i=0;i<len;i++) { if(i == len - 1) { read_data[i] = spi_swap_byte(spi_num,send_data[i],SPI_RETURN_PCS); } else { read_data[i] = spi_swap_byte(spi_num,send_data[i],SPI_KEEP_PCS); } } }
spi.h文件
/** * @file spi.h * @brief spi驱动 * @details 适配MPC5744P * @author loopy * @date 2026-08-16 * @version V1.0 * @note 无 */ #ifndef PERIPHERALS_SPI_H_ #define PERIPHERALS_SPI_H_ //头文件包含 #include "MPC5744P.h" //宏定义 #define SPI_PCS0 0b00000001 //片选0 #define SPI_PCS1 0b00000010 //片选1 #define SPI_PCS2 0b00000100 //片选2 #define SPI_PCS3 0b00001000 //片选3 #define SPI_PCS4 0b00010000 //片选4 #define SPI_PCS5 0b00100000 //片选5 #define SPI_PCS6 0b01000000 //片选6 #define SPI_PCS7 0b10000000 //片选7 //数据类型定义 typedef enum //SPI序号 { SPI0 = 0x0, SPI1 = 0x1, SPI2 = 0x2, SPI3 = 0x3, SPI_MAX = 0x4, }spi_num; typedef enum //SPI极性 { SPI_POL_LOW = 0x0, //SCK空闲状态为低电平 SPI_POL_HIGH = 0x1, //SCK空闲状态为高电平 }spi_pol; typedef enum //SPI相位 { SPI_PHA_0 = 0x0, //在 SCK第一个边沿捕获数据,在下一个边沿更新数据 SPI_PHA_1 = 0x1, //在 SCK第一个边沿更新数据,在下一个边沿捕获数据 }spi_pha; typedef enum //SPI 片选保持 { SPI_RETURN_PCS = 0x0, //一个字节传输完成片选恢复 SPI_KEEP_PCS = 0x1, //保持片选 }spi_keepcs; typedef enum //SPI BR系数 { SPI_BR_2 = 0x0, SPI_BR_4 = 0x1, SPI_BR_6 = 0x2, SPI_BR_8 = 0x3, SPI_BR_16 = 0x4, SPI_BR_32 = 0x5, SPI_BR_64 = 0x6, SPI_BR_128 = 0x7, SPI_BR_256 = 0x8, SPI_BR_512 = 0x9, SPI_BR_1024 = 0xA, SPI_BR_2048 = 0xB, SPI_BR_4096 = 0xC, SPI_BR_8192 = 0xD, SPI_BR_16384 = 0xE, SPI_BR_32768 = 0xF, }spi_br; //全局函数声明 /** * @brief spi初始化函数 * @param spi_num: SPI模块编号 * @param spi_pcs: 启用的片选引脚 * @param spi_pol: SCK的极性 * @param spi_pha: 相位 * @param spi_br: 分频系数 * @return 无 * @note 无 */ extern void spi_init(spi_num spi_num,uint8_t spi_pcs,spi_pol spi_pol,spi_pha spi_pha,uint8_t spi_br); /** * @brief spi发送接收函数 * @param spi_num: SPI模块编号 * @param byte: 传输的byte * @param spi_keepcs: 启用的片选引脚 * @return recdata: 读取返回值 * @note 无 */ extern uint8_t spi_swap_byte(spi_num spi_num,uint8_t byte,spi_keepcs spi_keepcs); /** * @brief spi发送接收字符串函数 * @param spi_num: SPI模块编号 * @param *send_data: 发送的数组 * @param *read_data: 接收的数组 * @param len: 数组长度 * @return 无 * @note 无 */ void spi_swap_string(spi_num spi_num,uint8_t *send_data,uint8_t *read_data,uint8_t len); #endif /* PERIPHERALS_SPI_H_ */

(五)实验现象

与W25Q64发0x9F,波形如下:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询