STM32单片机用GPIO模拟SPI收发

        MCU的SPI等通讯接口,需要使用官方的开发库,需要将接口定义在固定复用IO上,如下图所示。

         在实际使用中,经常会因为硬件设计错位或者主从时序不一致等原因导致通讯异常,这时可以使用GPIO模拟一个SPI的收发,实现通讯。

需求案例:

        MCU作为Master device,通过4线SPI与Slave device通讯;

        NSS设计为PA0(output),与slave device的CS连接;

        SCK设计为PA1(output) ,与slave device的SCLK连接;

        MISO设计为PA2(input) ,与slave device的SDO连接;

        MOSI设计为PA3(output) ,与slave device的SDI连接;

        Slave器件要求的时序如下图,15位地址和8位数据,最高位为读写控制,写时为低,读时为高。CS拉低时开始读写操作,SCK的下降沿master device开始往slave device写入地址和数据;读取时,master device先写入要读取的寄存器地址,然后slave device在SCK开始为低时向master device回复数据。

        注:SPI规定了两个SPI设备这几件通讯必须由主设备来控制从设备,主设备可以通过提供时钟和片选来控制多个从设备。从设备的时钟必须是由主设备的时钟管脚提供,从设备不能产生和控制时钟。

程序实现:

1、初始化GPIO

          /*Configure GPIO pin : PA0 PA1 PA3*/

          GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3;

          GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

          GPIO_InitStruct.Pull = GPIO_NOPULL;//是否上拉,取决于外围电路

          GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

          HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

          /*Configure GPIO pins : PA2*/

          GPIO_InitStruct.Pin = GPIO_PIN_2;

          GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

          GPIO_InitStruct.Pull = GPIO_NOPULL; //是否上拉,取决于外围电路

          HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

2、设计SPI延时,使用代码执行时间做延时;

void SPI_Delay(void)

{

     uint16_t  cnt = 5;

       while(cnt--);

}

3、设计SPI Master写函数

void SPI_Write(uint16_t addr,uint8_t value)

{

    uint32_t TxData;

    uint8_t w_cnt;

    //写数据拼接,W最高位为低

   TxData= (0x000000|(addr<<8)|value);

    //<使能>PA0,低有效

    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);

    SPI_Delay();

   for(w_cnt=0;w_cnt<24;w_cnt++)

   {

        //<时钟>PA1,拉低

        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

        //判断待写入数据是0还是1

        if(TxData &0x800000)

        {

                //待写入数据为1,将MOSI拉高

                HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

        }

        else

        {

               //待写入数据为0,将MOSI拉低

                HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

        }

        SPI_Delay();

        //待写入数据移至下一位

        TxData <<= 1;

        //<时钟>PA1,拉高

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,GPIO_PIN_SET);

        SPI_Delay();

   }

    //写完成,<使能>PA0拉高

    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);

    SPI_Delay();

}

4、设计SPI Master写函数

uint8_t SPI_Read(uint16_t addr)

{

   //读最高位为1

   addr =0x8000| addr;

   uint8_t w_cnt,r_cnt;

   uint8_t value,read_data;

   //<使能>PA0,低有效

   HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);

   //判断待写入数据是0还是1

   for(w_cnt=0;w_cnt<16;w_cnt++)

   {

       //<时钟>PA1,拉低

        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

        //判断待写入数据是0还是1

        if(addr &0x8000)

        {

                //待写入数据为1,将MOSI拉高

                HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

        }

        else

        {

               //待写入数据为0,将MOSI拉低

               HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);

        }

        SPI_Delay();

        //待写入数据移至下一位

        addr <<= 1;

        //<时钟>PA1,拉高

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,GPIO_PIN_SET);

        SPI_Delay();

 }

 for(r_cnt=8;r_cnt>0;r_cnt--)

 {

        //<时钟>PA1,拉低

        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);

        SPI_Delay();

        //从SDO中读出当前bit数据

        read_data=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_2);

       //将读出的数据移位与之前读出的数据进行拼接

        read_data=read_data<<(r_cnt-1);

        value=value|read_data;

       //<时钟>PA1,拉高

        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);

        SPI_Delay();

   }

  //读完成,<使能>PA0拉高

   HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);

   SPI_Delay();

   //读完成,返回读值

   return value;

}

5、三线SPI或者其他的时序要求都可以按照对应的要求进行模拟;速率跟主频及延时相关,需要根据实际应用测试;实际应用时可以将CS/SCK/SDI对应的GPIO控制#define一下,方便阅读。

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值