在开始使用GD32F350G8的硬件SPI时对于NSS功能下的主从模式有点迷惑,
需求为主机模式下对从机发送数据,片选线低有效
手册上说If the application wants to use NSS line to control the SPI slave, NSS should be configured to hardware output mode (SWNSSEN=0, NSSDRV=1). NSS goes low after SPI is enabled.
这里的问题是在开启NSS硬件输出模式后,使能SPI,NSS引脚是一直低的状态,不能满足需求,但也说到可以选择通用IO来作为片选线:
The application may also use a general purpose IO as NSS pin to realize more flexible NSS.
疑惑点在于我的SPI控制线是采用对应硬件SPI的IO口,
后面发现可以不配置NSS片选线,直接控制NSS电平状态
void SPI1_PortInit(void)
{
spi_parameter_struct spi_init_struct;
rcu_periph_clock_enable(RCU_SPI0);
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
gpio_af_set(GPIOB,GPIO_AF_0,GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5); //sck, MISO, MOSI
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_15); //nss
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5); //sck, MISO, MOSI
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_15); //nss
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5); //sck, MISO, MOSI
GPIO_BOP(GPIOA) = (uint32_t)GPIO_PIN_15; //set High
spi_i2s_deinit(SPI0);
/* configure SPI0 parameter */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; //全双工
spi_init_struct.device_mode = SPI_MASTER; //主机模式
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; //8bit
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.nss = SPI_NSS_SOFT; //软件触发
spi_init_struct.prescale = SPI_PSC_8;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI0, &spi_init_struct);
spi_enable(SPI0);
}
最开始我是直接这样操作:
void SPI1_Hardw_SendData(uint8_t Addr,uint8_t Regdata,uint32_t TimeOut)
{
GPIO_BC(GPIOA) = (uint32_t)GPIO_PIN_15;
while( RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) )
{
if(TimeOut > 0) TimeOut--;
else break;
}
Addr = Addr | 0x80; //地址高位置1,表示写寄存器
spi_i2s_data_transmit(SPI0,Addr);
while( RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) )
{
if(TimeOut > 0) TimeOut--;
else break;
}
spi_i2s_data_transmit(SPI0,Regdata);
GPIO_BOP(GPIOA) = (uint32_t)GPIO_PIN_15;
}
出来的波形错误,片选线被提前拉高,个人见解是数据在放入SPI_DATA寄存器后,SPI硬件自行去执行发数与时钟跳变这一些动作,而在程序上,数据存入SPI_DATA寄存器,就去执行片选线拉高动作,导致时序不正确,当时忘了记录现象;增加延时改进后就ok了
void SPI1_Hardw_SendData(uint8_t Addr,uint8_t Regdata,uint32_t TimeOut)
{
GPIO_BC(GPIOA) = (uint32_t)GPIO_PIN_15;
while( RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) )
{
if(TimeOut > 0) TimeOut--;
else break;
}
Addr = Addr | 0x80; //地址高位置1,表示写寄存器
spi_i2s_data_transmit(SPI0,Addr);
while( RESET == spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) )
{
if(TimeOut > 0) TimeOut--;
else break;
}
spi_i2s_data_transmit(SPI0,Regdata);
delay_1us(2); //延时等待SPI发送完成
GPIO_BOP(GPIOA) = (uint32_t)GPIO_PIN_15;
}
这里延时可以改进为判断SPI发送缓冲区空标志位(TBE)的状态来等待发送完成,发送缓冲区为空时, TBE置位。 通过写SPI_DATA寄存器将下一个待发送数据写入发送缓冲区来清除TBE置位。
当发送数据较多时,就可以引入SPI+DMA的方式来发送数据,为CPU减轻工作负担,去做其他事情。