STM32开发之SPI 与W25Q128通信

1、背景

调试STM32L051的SPI2 和 W25Q128 通信问题。问题是SPI的SCK一直高电平,没有信号输出,一直高;MOSI有信号输出,MISO一直是高电平,没有信号输出。GPIO的初始化和SPI的初始化,基本没有什么异常。

1.1 参考资料

STM32和ad7606的spi调试心得 https://blog.csdn.net/guangzhongyang/article/details/79029084

原子哥 寄存器版代码

2、解决方法

方法:GPIO 配置有问题,GPIO没有配置为上拉,以及GPIO_Speed。

因此有必要对基于STM32中外设的GPIO设置做一个总结。

3、SPI外设的配置

3.1 GPIO的配置

全双工SPI作为主机时,片选可以使硬件控制也可以使软件控制;硬件控制时(SCLK/MOSI/MISO/CSS)均配置为复用、输出速度为高,设置为内部上拉;软件控制时(SCLK/MOSI/MISO)均配置为复用、输出速度为高,设置为内部上拉,(CSS)配置为推挽输出模式,速度高,内部上拉。

GPIO的设置可由GPIO_Set和GPIO_AF_Set两个函数。

下面是GPIO_Set函数的具体内容: note注意:在输入模式(普通输入/模拟输入)下,OTYPE和OSPEED参数无效!!

#define GPIO_PIN0_SELECT    (0x00000001)
#define GPIO_PIN0_SELECT    (0x00000002)
...
#define GPIO_PIN15_SELECT   (0x00008000)
#define
/*
@fun: GPIO_Set
@note: GPIO通用设置 
@param: GPIOx:GPIOA~GPIOI.待设置的IO组
BITx:0X0000~0XFFFF,位设置,每个位代表一个IO,第0位代表Px0,第1位代表Px1,
MODE:0~3;模式选择,0,输入(系统复位默认状态);1,普通输出;2,复用功能;3,模拟输入.
OTYPE:0/1;输出类型选择,0,推挽输出;1,开漏输出.
OSPEED:0~3;输出速度设置,0,2Mhz;1,25Mhz;2,50Mhz;3,100Mh. 
PUPD:0~3:上下拉设置,0,不带上下拉;1,上拉;2,下拉;3,保留.
note注意:在输入模式(普通输入/模拟输入)下,OTYPE和OSPEED参数无效!!
*/
void GPIO_Set(GPIO_TypeDef* GPIOx,u32 BITx,u32 MODE,u32 OTYPE,u32 OSPEED,u32 PUPD)
{
    uint32_t pinNum = 0, curPinPos = 0;  //pin脚数(0~15);当前要检查的pin脚位置;
    for(pinNum = 0;pinNum < 16; pinNum++)
    {
         curPinPos = BITx & (((uint32_t)0x01)<MODER &= ~(3<<(pinNum*2));   //先清除原来的设置
               GPIOx->MODER |= MODE<<(pinNum*2);  //设置新的模式
               if((MODE==0x01) || (MODE==0x02))   //若是输出模式/复用模式
                {
                         GPIOx->OSPEEDR &= ~(3<<(pinNum*2));   //清除原来的设置
                         GPIOx->OSPEEDR |= (OSPEED<<(pinNum*2));    //设置新的速度值
                         GPIOx->OTYPER &= ~(1<OTYPER |= OTYPE<PUPDR &= ~(3<<(pinNum*2));    //先清除原来的位置
                GPIOx->PUPDR |= PUPD<<(pinNum*2);  //设置新的上下拉
         }
     }
}

GPIO复用设置函数 GPIO_AF_Set

/*
@fun:GPIO_AF_Set
@note: GPIO复用设置
@param:
GPIOx:GPIOA~GPIOI.
BITx:0~15,代表IO引脚编号.(15%8)
AFx:0~15,代表AF0~AF15.
*/
void GPIO_AF_Set(GPIO_TypeDef* GPIOx,u8 BITx,u8 AFx)
{  
    GPIOx->AFR[BITx>>3]&=~(0X0F<<((BITx&0X07)*4));  //对指定的位置域清零
    GPIOx->AFR[BITx>>3]|=(u32)AFx<<((BITx&0X07)*4);  //对指定的位置域设置复用类型
}

3.2 SPI的配置

1、设置为双线全双工方向;

2、设置SPI主机工作模式;

3、设置SPI的数据大小为8位帧结构;

4、串行同步时钟的空闲状态CPOL为低电平、串行同步时钟的跳变沿设置CPHA位;

5、从器件软件管理设置;

6、设置波特率预分频以定义串行时钟波特率

7、数据传输从低位还是高位开始;

8、CRC值计算的多项式设定。

9、使能SPI设备。

3.3 SPI的配置具体(寄存器版)

3.3.1、STM32F4xx的SPI2主模式配置(以STM32F407VG为例)

下面是初始化过程:

uint16_t tempReg = 0;
//SPI的GPIO配置PB12~15(NSS/SCK/MISO/MOSI)四个引脚
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;  //使能PORTB时钟
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;  //使能SPI2时钟使能
GPIO_Set(GPIOB,GPIO_PIN13_SELECT | GPIO_PIN14_SELECT | GPIO_PIN15_SELECT 2,0,3,1);
GPIO_Set(GPIOB,GPIO_PIN12_SELECT,1,0,3,1);  //普通输出,作为片选使能脚
GPIO_AF_Set(GPIOB,GPIO_PIN13_SELECT ,5); //pin13~pin15 复用为SPI2
GPIO_AF_Set(GPIOB,GPIO_PIN14_SELECT ,5);
GPIO_AF_Set(GPIOB,GPIO_PIN15_SELECT ,5);
//SPI2 初始化
RCC->APB1RSTR |= RCC_APB1RSTR_SPI2RST;  //复位SPI2
RCC->APB1RSTE &= ~RCC_APB1RSTR_SPI2RST; //停止复位SPI2
//作为主机、选择软件控制从器件选择、时钟相位设为第二个沿采样、时钟极性为空闲时高电平、256分频、使能
//SPI、双工模式(收发)、8位数据、从MSB先发送
tempReg = (SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_BR | SPI_CR1_SPE)\
          & ~(SPI_CR1_RXONLY | SPI_CR1_DFF | SPI_CR1_LSBFIRST);
SPI2->CR1 = tempReg;
SPI2->I2SCFGR &= ~SPI_I2SCFGR_I2SMOD; //选择SPI模式
while((SPI2->SR & (SPI2_SR_TXE))==RESET)
;
SPI2->DR = 0xFF;
while((SPI2->SR & (SPI2_SR_RXNE))==RESET)
;
SPI2->DR;

通用的SPI速度设置

/*
@fun: SPI2_SetSpeed
@note: SPI1速度设置函数
@param:
SpeedSet:0~7
SPI速度=fAPB2/2^(SpeedSet+1)
fAPB1时钟一般为42Mhz
*/
void SPI2_SetSpeed(uint8_t SpeedSet)
{
    SPI2->CR1 &= ~(SPI_CR1_SPE);  //除能SPI
    SpeedSet &= 0x07;
    SPI2->CR1 &= ~(SPI_CR1_BR);      //清除BR
     SPI2->CR1 |= SpeedSet<<3;  //设置SPI速度
     SPI2->CR1 |= SPI_CR1_SPE; //使能SPI
} 

 3.3.2 STM32L0xx的SPI主模式配置(以STM32L051C6为例)

基本流程一致。

 

你可能感兴趣的