STM32F030使用硬件SPI与字库芯片通信
前言
在淘宝买了350-019-PC 3.5寸TFT彩屏,卖家提供了DEMO程序是软件模拟SPI的,但这种程序只能用于演示,实际使用其通讯速率远远不能够满足要求。因此在转换为STM32自带的SPI通讯的时候,ST7796S驱动由于是只发送不接受,因此很快就能够调通,但是模块上的字库芯片是就调试了好几天都一直无法调通,网上也查询了很多资料依然无法解决,最后是给询问我同事之后才终于解决,最后才知道我对硬件SPI有有误解才导致了这次的问题,为了让自己长记性,因此写了这个博文。
基本信息
单 片 机: STM32F030C8Tx
通信接口:SPI2
字库芯片:JLX-GB2312-3205
SPI的代码
void SPI2_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF ;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_Level_3;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_0);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_0);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_I2S_DeInit(SPI2);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_RxFIFOThresholdConfig(SPI2, SPI_RxFIFOThreshold_QF);
SPI_Cmd(SPI2, ENABLE);
}
void SPI2_WriteByte(uint8_t data)
{
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_SendData8(SPI2, data);
}
uint8_t SPI2_ReadByte(void)
{
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_ReceiveData8(SPI2);
}
uint8_t SPI2_Transfer(uint8_t data)
{
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_SendData8(SPI2, data);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_ReceiveData8(SPI2);
}
字库驱动代码
/****送指令到晶联讯字库IC***/
void send_command_to_ROM( u16 datu )
{
#ifdef LCD_USE_HW_SPI
//SPI2_WriteByte(datu);
SPI2_Transfer(datu);
#else
u16 i;
for(i=0;i<8;i++ )
{
if(datu&0x80)
ROM_MOSI_Set();
else
ROM_MOSI_Clr();
datu = datu<<1;
ROM_CLK_Clr();
ROM_CLK_Set();
}
#endif
}
/****从晶联讯字库IC中取汉字或字符数据(1个字节)***/
static u8 get_data_from_ROM(void)
{
#ifdef LCD_USE_HW_SPI
return SPI2_Transfer(0x00);
//return SPI2_ReadByte();
#else
u16 i;
u16 ret_data=0;
ROM_CLK_Set();
for(i=0;i<8;i++)
{
ROM_MISO_Set();
ROM_CLK_Clr();
ret_data=ret_data<<1;
if( ROM_MISO_Get())
ret_data=ret_data+1;
else
ret_data=ret_data+0;
ROM_CLK_Set();
}
return(ret_data);
#endif
}
字库通信代码
void get_and_write_32x32(u32 fontaddr,u16 x,u16 y,u16 font_color,u16 back_color)
{
u16 i,j,disp_data;
ROM_CS_Clr();
send_command_to_ROM(0x03);
send_command_to_ROM((fontaddr&0xff0000)>>16); //地址的高8位,共24位
send_command_to_ROM((fontaddr&0xff00)>>8); //地址的中8位,共24位
send_command_to_ROM(fontaddr&0xff); //地址的低8位,共24位
for(j=0;j<32;j++)
{
lcd_address(y,x+j,32,32);
for(i=0;i<4;i++)
{
disp_data=get_data_from_ROM();
mono_data_out(disp_data,font_color,back_color); //这一句相当于写了一行8个像素点的数据。
}
}
ROM_CS_Set();
}
主要问题点:
- 可以从上面的代码看到,在通信的过程中,会先发送三个字节的地址,接着就一直读取数据。
- 如果字库的读取驱动按照我屏蔽的方式去通信,由于SPI是全双工通信,那么在发送完三个字节的地址数据之后,接着一直调用读取的接口,此时只会读取到一个数据,接着就读取不到数据,则一直会进入到等待数据的死循环中while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET),主要原因是因为调用SPI_ReceiveData8(SPI2)是直接从寄存器中获取数据,此时是不产生时钟信号的,这点就是我一直误解的点,因此想要产生时钟数据则需要每次在读取前发送个0x00数据才能够有时钟信号,进行全双工通信正常获取到数据。