实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式

目标:

实现spi的Master端。

参考资料:

http://bbs.elecfans.com/jishu_441914_1_1.html

这篇文章讲的非常直观易懂,看完就明白了。

 

1、首先配置口线,串口用来输入和打印,四个io口用来模拟spi,原本准备在一个单片机上同时模拟主从,但貌似不太可行,这种模拟的方式都是阻塞的。

实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式_第1张图片

 

2、proteus连接口线,放spi调试器和示波器,本来准备放个逻辑分析仪,但既然有spi调试器,用起来更方便,就没弄逻辑分析仪。

实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式_第2张图片

 

3、打开cubemx生成的工程,首先修改 main.h,加入口线的控制函数:

/* Private defines -----------------------------------------------------------*/
#define MASTER_CLK_Pin GPIO_PIN_1
#define MASTER_CLK_GPIO_Port GPIOC
#define MOSI_Pin GPIO_PIN_2
#define MOSI_GPIO_Port GPIOC
#define MISO_Pin GPIO_PIN_3
#define MISO_GPIO_Port GPIOC
#define CS_Pin GPIO_PIN_4
#define CS_GPIO_Port GPIOC
/* USER CODE BEGIN Private defines */


#define SCLK_SET_H  HAL_GPIO_WritePin(MASTER_CLK_GPIO_Port, MASTER_CLK_Pin, GPIO_PIN_SET)
#define SCLK_SET_L  HAL_GPIO_WritePin(MASTER_CLK_GPIO_Port, MASTER_CLK_Pin, GPIO_PIN_RESET)
#define MOSI_SET_H  HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET)
#define MOSI_SET_L  HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET)
#define MISO_READ   HAL_GPIO_ReadPin(MISO_GPIO_Port, MISO_Pin)
#define CS_H        HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET)
#define CS_L        HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET)

uint8_t SPI_RW_BYTE_MODE0(uint8_t data);
uint8_t SPI_RW_BYTE_MODE1(uint8_t data);
uint8_t SPI_RW_BYTE_MODE2(uint8_t data);
uint8_t SPI_RW_BYTE_MODE3(uint8_t data);
void SPI_WRITE_BUF_MODE0(uint8_t *pData,int len);
void SPI_WRITE_BUF_MODE1(uint8_t *pData,int len);
void SPI_WRITE_BUF_MODE2(uint8_t *pData,int len);
void SPI_WRITE_BUF_MODE3(uint8_t *pData,int len);

4、在主函数中,实现四种模式的单字节收发函数:

在实现函数之前,请了解spi协议的时序,以及四种模式,本文侧重仿真测试,不做具体原理讲解:

CPOL = 0 时,时钟在逻辑 0 处空闲:
MODE0: CPOL = 0,CPHA = 0,数据会在 SCK的 上升沿采样,下降沿变化。
MODE1: CPOL = 0,CPHA = 1,数据会在 SCK的 下降沿采样,上升沿变化。

CPOL = 1时,时钟在逻辑高电平处空闲:
MODE2: CPOL = 1,CPHA = 0,数据会在 SCK的 下降沿采样,上升沿变化。
MODE3: CPOL = 1,CPHA = 1,数据会在 SCK的 上升沿采样,下降沿变化。

/*
MODE0: CPOL = 0,CPHA = 0,数据会在 SCK的 上升沿采样,下降沿变化。
sck:
          --------      --------
          |      |      |      |
        ---      -------

mosi:
        ----------          
                 |             |
                 ---------------

miso:
        ----------          
                 |             |
                 ---------------

*/
uint8_t SPI_RW_BYTE_MODE0(uint8_t data)
{
    uint8_t i;
    uint8_t output=0;

    SCLK_SET_H; 
    SPIDelay;
    CS_L;
    SPIDelay;
    
    for(i=0;i<8;i++)
    {   
        SCLK_SET_L;  
        SPI_SEND_BIT(data);  //下降沿发送
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间

        SCLK_SET_H; 
        SPI_READ_BIT(output);//上升沿采样,注意,必须先准备好数据,再才升上升沿,否则会出现第一个包不正确的情况。
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间        
    }

    SCLK_SET_L; 
    SPIDelay;
    CS_H;
    SPIDelay;
    return output;          
}
/*
MODE1: CPOL = 0,CPHA = 1,数据会在 SCK的 下降沿采样,上升沿变化。
sck:
          --------      -------
          |      |      |
        ---      -------

mosi:
        ---             -------
          |_____________|

miso:
        ---             -------
          |_____________|

*/                 
uint8_t SPI_RW_BYTE_MODE1(uint8_t data)
{
    uint8_t i;
    uint8_t output=0;

    SCLK_SET_L;  
    SPIDelay;
    CS_L;
    SPIDelay;
    
    for(i=0;i<8;i++)
    {   
        SCLK_SET_H;
        SPI_SEND_BIT(data);  //上升沿发送
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间
         
        SCLK_SET_L; 
        SPI_READ_BIT(output);//下降沿采样
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间
    }

    SCLK_SET_L;
    SPIDelay;
    CS_H;
    SPIDelay;
    
    return output;          
}
/*
MODE2: CPOL = 1,CPHA = 0,数据会在 SCK的 下降沿采样,上升沿变化。
sck:
        ---      --------
          |      |      |
          --------      ---
mosi:
        ---------           
                 |
                 ----------

miso:
        ---------           
                 |
                 ----------
*/          

uint8_t SPI_RW_BYTE_MODE2(uint8_t data)
{
    uint8_t i;
    uint8_t output=0;

    SCLK_SET_L; 
    SPIDelay;
    CS_L;
    SPIDelay;
    
    for(i=0;i<8;i++)
    {   
        SCLK_SET_H;             
        SPI_SEND_BIT(data);  //上升沿发送
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间
        
        SCLK_SET_L;     
        SPI_READ_BIT(output);//下降沿采样
        SPIDelay;            //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间
    }

    SCLK_SET_H;
    SPIDelay;
    CS_H;
    SPIDelay;
    
    return output;          
}

 

 

/*
MODE3: CPOL = 1,CPHA = 1,数据会在 SCK的 上升沿采样,下降沿变化。
sck:
        ---      --------
          |      |      |
          --------      ---
          
mosi:
        ---             ---
          |             |
          ---------------
          
miso:
        ---             ---
          |             |
          ---------------  

*/

uint8_t SPI_RW_BYTE_MODE3(uint8_t data)
{
    uint8_t i;
    uint8_t output=0;

    SCLK_SET_H; 
    SPIDelay;
    CS_L;
    SPIDelay;
    
    for(i=0;i<8;i++)
    {   
        SCLK_SET_L;              
        SPI_SEND_BIT(data);   //下降沿发送
        SPIDelay;             //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间

        SCLK_SET_H;             
        SPI_READ_BIT(output); //上升沿采样
        SPIDelay;             //每次时钟变化之前,进行delay,确保数据稳定,对方有足够的采样时间
    }

    SCLK_SET_H; 
    SPIDelay;
    CS_H;
    SPIDelay;
    
    return output;          
}

5、修改串口测试程序,用于发送spi命令:

switch(aRxBuffer){
        case 'a':
            ret=SPI_RW_BYTE_MODE0(0x0a);
            snprintf(( char *)output,sizeof(output),"mode0,ret=0x%02x\r\n",ret);
            len  = strlen(output);			
            break;
        case 'b':
            ret=SPI_RW_BYTE_MODE1(0x0b);
            snprintf(( char *)output,sizeof(output),"mode1,ret=0x%02x\r\n",ret);
            len  = strlen(output);	
            break;
        case 'c':
            ret=SPI_RW_BYTE_MODE2(0x0c);
            snprintf(( char *)output,sizeof(output),"mode2,ret=0x%02x\r\n",ret);
            len  = strlen(output);	
            break;
        case 'd':
            ret=SPI_RW_BYTE_MODE3(0x0d);
            snprintf(( char *)output,sizeof(output),"mode3,ret=0x%02x\r\n",ret);
            len  = strlen(output);	
            break;    
        case 'e':
            SPI_WRITE_BUF_MODE0(buf,sizeof(buf));
            goto END;
        case 'f':
            SPI_WRITE_BUF_MODE1(buf,sizeof(buf));
            goto END;     
        case 'g':
            SPI_WRITE_BUF_MODE2(buf,sizeof(buf));
            goto END; 
        case 'h':
            SPI_WRITE_BUF_MODE3(buf,sizeof(buf));
            goto END;            
        default:
            snprintf(( char *)output,sizeof(output),"no supported command\r\n");
            len  = strlen(output);	
            break;
	}

6、仿真测试结果:

实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式_第3张图片

 

7、可以双击spi访问器件,修改极性和相位,分别测试四种模式:

实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式_第4张图片

 

8、在上述工程基础上,加上buffer写入的方式:

void SPI_WRITE_BUF_MODE0(uint8_t *pData,int len)
{
    int i,j;
    uint8_t data = 0;

    SCLK_SET_H; 
    SPIDelay;
    CS_L;
    SPIDelay;

    for(i=0;i

9、压力测试结果:

实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式_第5张图片

256个字节确认收到无误!

以上是整个实验过程,相关代码和仿真设计,请到如下地址下载(没积分也是苦恼,为了能下点东西,象征性收取1个积分,请见谅!):

https://download.csdn.net/download/qq_39657229/10918641

你可能感兴趣的:(实验3:CubeMx+Proteus+STM32 IO口模拟SPI协议的四种模式)