CMS32L051 SPI分享

本文介绍了如何使用CMS32L051芯片进行SPI通信,包括I/O模拟SPI和硬件SPI的实现。I/O模拟SPI通过GPIO控制信号实现,详细展示了读取传感器数据的代码流程。硬件SPI则涉及初始化相关寄存器,文中给出了从机模式初始化的代码,并讨论了两者优缺点。提供了项目源码链接供参考。
摘要由CSDN通过智能技术生成

CMS32L051 SPI分享


–前言
前几天做了个项目用到了CMS32L051这款芯片,需要用SPI去读取传感器的数据并通过SPI转发给上位机。
读取传感器的SPI采用了用I/O(引脚)模拟的方式去实现的SPI通信,而与上位机的通信调用的是芯片本身硬件的SPI。
所以本文前半部分分享的是I/O模拟SPI的流程及代码,后半部分分享如何通过初始化相关寄存器来实现SPI通信。

一、SPI是什么?

SPI的详细解释我就不过多赘述了,网上的相关介绍还是非常详尽的。
我就针对四根线的具体含义解释一下(为模拟SPI铺垫一下):

  • SSN(片选):从设备使能信号,由主设备控制(本质上就是主机发送下拉信号)。
  • SCK(时钟):时钟信号,由主设备产生(本质上就是连续的下拉上拉信号)。
  • MISO:M主机S从机 I输入O输出(数据传输的本质也是高低电平 )。
  • MOSI:M主机S从机 I输入O输出(例如0x83 1000 0011 高电平为1低电平为0)。

二、I/O模拟SPI

1.SPI的本质

从我上文的铺垫可以看出,不管SSN信号、SCK时钟还是数据本质上都是高低电平的组合。这就给我们通过I/O去模拟SPI提供了事实基础。让我们来看一下SPI的时序图。
3线SPI时序图模拟SPI读取MT6815(磁性旋转编码器芯片)的流程为:
发送读取命令0x83,接收03寄存器的数据,发送读取命令0x84,接收04寄存器的数据。
让我们结合代码来看一看I/O模拟是怎么实现的

#define  SPI_OUTPUT_ENABLE() GPIO_Output_Enable(&PORT->P7, 0x1C)  // 0001 1100 P72 P73 P74 输出使能
#define  SPI_INPUT_ENABLE()  PORT_Init( PORT7, PIN4, PULLDOWN_INPUT);  //P74 默认上拉输入 需要设置为下拉输入

#define  SET_SPI_CSN()     GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) | 0x04 )  //P72   CSN 置1
#define  RESET_SPI_CSN()   GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) & ~0x04 ) //P72   CSN 置0
#define  SET_SPI_SCK()     GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) | 0x08 )  //P73   SCK 置1
#define  RESET_SPI_SCK()   GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) & ~0x08 ) //P73   SCK 置0
#define  SET_SPI_SDAT()    GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) | 0x10 )  //P74  SDAT 置1
#define  RESET_SPI_SDAT()  GPIO_Set_Value(&PORT->P7, GPIO_Get_Value(&PORT->P7) & ~0x10)  //P74  SDAT 置0

static unsigned short  s_usSPI_Data[3];

unsigned short  Read_Magnetic_Encode(void)   //产生clk信号,发送数据和接收数据
{
   unsigned char i;
   unsigned short j;
 
   s_usSPI_Data[0] = 0; //
   j =0x84;             //待发送的数据
 
   SPI_OUTPUT_ENABLE(); //P72 P73 P74 输出使能 SDAT为输出 准备发送读的命令0x83
   RESET_SPI_CSN();     //P72  CSN 拉低 发送开始前线拉低片选信号
 
   for(i = 0;i< 8;i ++) //为了不占太多行就if一句话的不加花括号了
   {
     RESET_SPI_SCK();   //P73  SCK 拉低   
     if(j&0x80)         //如果待发送的是1 就把P74数据线置高
      SET_SPI_SDAT();   //P74  SDAT 拉高 
     else               //否则 就把P74数据线置低
      RESET_SPI_SDAT(); //P74  SDAT 拉低
     j <<= 1;           //发完一位数据发下一位数据
     SET_SPI_SCK();     //P73  SCK 拉高
   } //发送完读的命令(0x83) 切换SDAT为输入 准备接收数据
    SPI_INPUT_ENABLE();   //P74 输入使能
    for(i = 0;i< 8;i ++)
    {
      RESET_SPI_SCK();   //P73  SCK 拉低
      s_usSPI_Data[0] <<= 1;
      if( (GPIO_Get_Value(&PORT->P7) & 1 << 4) != 0 ) //P74 SDAT 输入不为零
        s_usSPI_Data[0] += 1;
      SET_SPI_SCK();     //P73  SCK 拉高
    }
   SET_SPI_CSN();       //P72  CSN 拉高
   SPI_OUTPUT_ENABLE(); //P72 P73 P74 输出使能 SDAT为输出 准备发送读的命令0x84
   /************** 第二遍发送 接收 *************/
   j = 0x84;
   RESET_SPI_CSN();     //P72  CSN 拉低 发送开始前线拉低片选信号
   for(i = 0;i< 8;i ++) //为了不占太多行就if一句话的不加花括号了
   {
     RESET_SPI_SCK();   //P73  SCK 拉低   
     if(j&0x80)         //如果待发送的是1 就把P74数据线置高
      SET_SPI_SDAT();   //P74  SDAT 拉高 
     else               //否则 就把P74数据线置低
      RESET_SPI_SDAT(); //P74  SDAT 拉低
     j <<= 1;           //发完一位数据发下一位数据
     SET_SPI_SCK();     //P73  SCK 拉高
   } //发送完读的命令(0x83) 切换SDAT为输入 准备接收数据
    SPI_INPUT_ENABLE();   //P74 输入使能
    for(i = 0;i< 8;i ++)
    {
      RESET_SPI_SCK();   //P73  SCK 拉低
      s_usSPI_Data[0] <<= 1;
      if( (GPIO_Get_Value(&PORT->P7) & 1 << 4) != 0 ) //P74 SDAT 输入不为零
        s_usSPI_Data[0] += 1;
      SET_SPI_SCK();     //P73  SCK 拉高
    }
    SET_SPI_CSN();       //P72  CSN 拉高
}


最后展示一下I/O模拟SPI的波形图

SCL时钟信号
时钟+数据

二、硬件SPI

1.SPI从机模式初始化

让我们翻看一下CMS32L051用户手册第13章 串行接口SPI。
13.4.3从属的发送和接收
SPI初始化

废话不多说上代码

void SPI_SlaveInit(spi_mode_t mode)
{
//Clock Generate Control (CGC)
  //PER1 0x0000041A)外设使能寄存器1  PER10 外围设备启用寄存器0
    CGC->PER1 |= 0x80; // 提供SPI的输入时钟的控制    ***步骤 1***
//串行接口SPI (SPI) SPIC SPI控制寄存器  (0x0007U) /* 从SCK输入的外部时钟*/
    SPI->SPIC = _0007_SPI_SLAVE_MODE | (~mode & 0x03) << 3; //***步骤 2** **步骤 3***
   //SPI->SPIC = _0007_SPI_SLAVE_MODE | ( 0x03 ) << 3; 
//SPIM 0x00000000) SPI模式控制寄存器 
   // (0x0040U) transmission/reception                    buffer empty interrupt  (0x0004U) 16-bit data length
   SPI->SPIM = _0040_SPI_RECEPTION_TRANSMISSION | _0000_SPI_MSB |  _0008_SPI_BUFFER_EMPTY | _0004_SPI_LENGTH_16| _0080_SPI_START_TRG_ON;  // | _0020_SPI_NSS_ENABLE | _0080_SPI_START_TRG_ON;
 //***步骤 4***
                                                        
  PORT->SPIPCFG = 0x01; //将SPI通信兼用功能映射到 P50 P51 P16 P17
 
    NSS_PORT_SETTING();  // P50  NSS 输入 
    SCK_PORT_SETTING();  // P51  SCK 输入
    MOSI_PORT_SETTING(); //P16  M0SI    从机输入
    MISO_PORT_SETTING(); //P17  MISO    从机输出
//  结束初始设定如果给SDRO寄存器设定发送数据,  等待主控设备的时钟
   SPI->SDRO = 0x0000;
 
}

int main()
{
   uint32_t msCnt;  // count value of 1ms
   SystemCoreClockUpdata();
   msCnt = SystemCoreClock /1000 ;
   SysTick_Config(msCnt);
}

int main()
{
   uint32_t msCnt;  // count value of 1ms
   //Systick setting  相关性不强的系统配置按下不表
   SPI_SlaveInit(SPI_MODE_2); //SPI初始化 选模式二 即CPOL=1(极性),CPHA=0(相位)
   //关于极性和相位的具体含义大家可以自行搜索 
   //我参考的是这篇文章 https://www.sohu.com/a/157067231_505803
   GPIO_Output_Enable(&PORT->P1,GPIO_Get_Value(&PORT->P1) | 0x04 ); //P12 输出使能
   GPIO_Set_Value(&PORT->P1,GPIO_Get_Value(&PORT->P1) | 0x04 ); //P12置高 点灯
   
   while(1)
   {
     if((GPIO_Get_Value(&PORT->P5) == 0x02) //等待CS P50 下拉信号
     {
       GPIO_Set_Value( &PORT->P1, GPIO_Get_Value(&PORT->P1) & ~0x80 ); //P17
     //拉低(MISO) 这也是我被坑的最惨地方 在CS下拉信号之后竟然还需要给MISO一个下触发信号
     //这还是我乱试试出来的 完全不合逻辑的操作折磨了我一个星期
       while( (GPIO_Get_Value(&PORT->P5) & 0x01 )== 0x00)//CS 下拉信号持续时间
       { //主机在拿取 SPI->SDRO(发送缓冲寄存器) 中的数据
       }
       if((GPIO_Get_Value(&PORT->P5) == 0x03) //CS 下拉信号结束 从机数据发送完毕
       {
          SPI->SDRO = Read_Magnetic_Encode(); 
          //将模拟SPI接收的数据赋给SDRO 等待下一次CS的下拉信号发送给主机
       }
     }//if
   }//while
   
}//main


总结

  • 相对于硬件SPI , I/O模拟SPI的优势在于不需要了解芯片SPI的相关配置寄存器 。只要求掌握基础的GPIO输入与输出 和 SPI时序有一定的了解,在有现成的 时序逻辑代码 后移植起来非常的轻松。
  • 相比于I/O模拟SPI 硬件SPI的优势在于:
    1 输出的波形比较标准,抗干扰能力相较于I/O模拟SPI更强;
    2只需要配置好相关的寄存器,数据的收发将会无比轻松,只需要 读写 相关的 发送(SDRO)接收(SDRI)寄存器 。难点在于SPI的初始化配置。

项目源码链接

阿里云盘:「6816磁性编码器项目」https://www.aliyundrive.com/s/4ogh6XGsAUt 提取码: 4m8c
百度网盘:链接:https://pan.baidu.com/s/1gjs7E7tujoMb6NhcZdmlww 提取码:accg

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CMS32L051是一款低功耗、高性能的ARM Cortex-M0+内核微控制器。它具有丰富的外设和功能,适用于各种嵌入式应用场景。以下是一些关于CMS32L051学习笔记的介绍: 1. 硬件介绍:CMS32L051微控制器具有32位ARM Cortex-M0+内核,工作频率可达到50MHz。它包含了多个外设,如通用串行接口、通用定时器、模拟比较器等,可以满足各种应用需求。 2. 开发环境:为了进行CMS32L051的开发,你可以使用Keil MDK开发环境或者其他支持ARM Cortex-M系列的开发工具。Keil MDK提供了一套完整的开发工具链,包括编译器、调试器和仿真器,方便进行代码编写、调试和下载。 3. 编程语言:CMS32L051可以使用C语言进行编程。你可以通过编写C语言代码来控制微控制器的各个外设,并实现所需的功能。 4. 外设编程:CMS32L051具有丰富的外设,包括GPIO、UART、SPI、I2C等。你可以学习如何初始化和配置这些外设,并使用相应的库函数进行数据的读写和控制。 5. 中断处理:中断是嵌入式系统中常用的一种事件处理机制。你可以学习如何配置和处理CMS32L051的中断,以实现对外部事件的响应和处理。 6. 低功耗设计:CMS32L051具有低功耗特性,适用于电池供电的应用场景。你可以学习如何进行低功耗设计,包括使用低功耗模式、优化代码和外设的使用等。 7. 调试技巧:在开发过程中,调试是非常重要的一环。你可以学习如何使用调试器和仿真器进行代码的单步调试、变量查看和性能分析,以提高开发效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值