调试STM32L051的SPI2 和 W25Q128 通信问题。问题是SPI的SCK一直高电平,没有信号输出,一直高;MOSI有信号输出,MISO一直是高电平,没有信号输出。GPIO的初始化和SPI的初始化,基本没有什么异常。
STM32和ad7606的spi调试心得 https://blog.csdn.net/guangzhongyang/article/details/79029084
原子哥 寄存器版代码
方法:GPIO 配置有问题,GPIO没有配置为上拉,以及GPIO_Speed。
因此有必要对基于STM32中外设的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); //对指定的位置域设置复用类型
}
1、设置为双线全双工方向;
2、设置SPI主机工作模式;
3、设置SPI的数据大小为8位帧结构;
4、串行同步时钟的空闲状态CPOL为低电平、串行同步时钟的跳变沿设置CPHA位;
5、从器件软件管理设置;
6、设置波特率预分频以定义串行时钟波特率
7、数据传输从低位还是高位开始;
8、CRC值计算的多项式设定。
9、使能SPI设备。
下面是初始化过程:
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
}
基本流程一致。