4.1 SPI 驱动
高通字库芯片 SPI 通信驱动程序参考
4.1 硬件 SPI 驱动参考(ST标准库)
以下代码基于 STM32F4 标准外设库,配置 SPI1 与高通字库芯片通信。SCK/PA5, MISO/PA6, MOSI/PA7, CS/PA4。
spi_hardware.c
//SPI1 pin
#define MCU_SPI1_NSS_PIN GPIO_Pin_4 //PA4
#define MCU_SPI1_SCK_PIN GPIO_Pin_5 //PA5
#define MCU_SPI1_MISO_PIN GPIO_Pin_6 //PA6
#define MCU_SPI1_MOSI_PIN GPIO_Pin_7 //PA7
#define MCU_SPI1_CS3_PIN GPIO_Pin_7 //PB7
#define MCU_SPI1_PORT GPIOA
#define MCU_SPI1_CS3_PORT GPIOB
#define ZK_SPI1 SPI1
#define Rom_csH GPIO_WriteBit(MCU_SPI1_CS3_PORT, MCU_SPI1_CS3_PIN, Bit_SET)
#define Rom_csL GPIO_WriteBit(MCU_SPI1_CS3_PORT, MCU_SPI1_CS3_PIN, Bit_RESET)
/**
SPI1 初始化
*/
void SPI1_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI1_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB
| RCC_APB2Periph_AFIO | RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitStructure.GPIO_Pin = MCU_SPI1_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //GPIO_Mode_IPD GPIO_Mode_IN_FLOATING
GPIO_Init(MCU_SPI1_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = MCU_SPI1_SCK_PIN | MCU_SPI1_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MCU_SPI1_PORT, &GPIO_InitStructure);
#if 0 //硬件NSS, 该部分未测试
GPIO_InitStructure.GPIO_Pin = MCU_SPI1_NSS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(MCU_SPI1_PORT, &GPIO_InitStructure);
#else //软件模拟CS, 该部分可以正常调用
GPIO_InitStructure.GPIO_Pin = MCU_SPI1_CS3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MCU_SPI1_CS3_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = MCU_SPI1_NSS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(MCU_SPI1_PORT, &GPIO_InitStructure);
#endif
Rom_csH; //CS拉高
//SPI模式设置
SPI1_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工模式
SPI1_InitStructure.SPI_Mode = SPI_Mode_Master; //主设备模式
SPI1_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8字节数据通信
SPI1_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟空闲时为低电平 SPI_CPOL_Low
SPI1_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //时钟奇数边沿采样
SPI1_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制NSS(CSN)位
SPI1_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //通信速率设定,波特率预分频值 SPI_BaudRatePrescaler_256
SPI1_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB开始
SPI1_InitStructure.SPI_CRCPolynomial = 7; //CRC计算多项式
SPI_Init(ZK_SPI1, &SPI1_InitStructure); //初始化
SPI_Cmd(ZK_SPI1, ENABLE); //使能.
//Rom_csL;
//SPI1_ReadWriteByte(0xFF);
//Rom_csH;
}
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) {
}//等待发送区空
SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) {
} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
}
static void SPI_Address(unsigned char AddH, unsigned char AddM, unsigned char AddL)
{
SPI1_ReadWriteByte(AddH);
SPI1_ReadWriteByte(AddM);
SPI1_ReadWriteByte(AddL);
}
//客户自己实现,从address地址读取len个字节的数据并存入到DZ_Data数组当中
unsigned long r_dat_bat(unsigned long address, unsigned long DataLen, unsigned char * pBuff)
{
unsigned long i;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
addrHigh = address >> 16;
addrMid = address >> 8;
addrLow = (unsigned char)address;
Rom_csL; //片选选中字库芯片
SPI1_ReadWriteByte(0x03); //普通读取首先送0X03,然后发送地址高八位addrHigh,中八位addrMid,低八位addrLow。
SPI_Address(addrHigh, addrMid, addrLow);
for (i = 0;i < DataLen;i++)
*(pBuff + i) = SPI1_ReadWriteByte(0x00);
Rom_csH;
return i;
}
unsigned char CheckID(unsigned char CMD, unsigned long address,
unsigned long byte_long, unsigned char * p_arr)
{
unsigned long j = 0;
Rom_csL;
SPI1_ReadWriteByte(CMD);
SPI1_ReadWriteByte((unsigned char)((address) >> 16));
SPI1_ReadWriteByte((unsigned char)((address) >> 8));
SPI1_ReadWriteByte((unsigned char)address);
for (j = 0;j < byte_long;j++) {
p_arr[j] = SPI1_ReadWriteByte(0x00);
}
Rom_csH;
return 1;
}
//客户自己实现,从address地址读取一个字节的数据并返回该数据
/**
* @brief 从address地址读取一个字节的数据
*
* @param address 数据所在地址
* @return unsigned char 该地址的具体数据
*/
unsigned char r_dat(unsigned long address)
{
unsigned char buff;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
addrHigh = address >> 16;
addrMid = address >> 8;
addrLow = (unsigned char)address;
Rom_csL; //片选选中字库芯片
SPI1_ReadWriteByte(0x03); //普通读取首先送0X03,然后发送地址高八位addrHigh,中八位addrMid,低八位addrLow。
SPI_Address(addrHigh, addrMid, addrLow);
buff = SPI1_ReadWriteByte(0x00);
Rom_csH;
return buff;
}
4.2 软件模拟 SPI 驱动参考
无需硬件 SPI 外设,GPIO 模拟实现
软件模拟 SPI
当 MCU 硬件 SPI 资源不足或需要更灵活的引脚分配时,可用 GPIO 模拟 SPI 时序。 以下代码适配任意平台,仅需 4 个 GPIO 引脚。
spi_soft.c
#define Rom_csH P1_1 = 1
#define Rom_csL P1_1 = 0
#define MOSIH P1_2 = 1
#define MOSIL P1_2 = 0
#define Rom_sckH P1_3 = 1
#define Rom_sckL P1_3 = 0
#define MISO P1_4
void zk_init(void)
{
Rom_csH;
MOSIH;
Rom_sckH;
}
// Send data sub-pro (STM8,STM32等双向口) SPI发送地址的时序算法
void Send_Byte(unsigned char out)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
Rom_sckL; //字库芯片时钟置低
if(((out<<i)&0x80)==0)
MOSIL;
else
MOSIH;
Rom_sckH;
}
}
// Get data sub-pro (STM8,STM32等双向口) SPI接收点阵数据的算法
unsigned char Get_Byte(void)
{
unsigned char i;
unsigned char read_dat;
Rom_sckH;
for(i=0;i<8;i++)
{
Rom_sckL;
read_dat=read_dat<<1;
if(MISO)
read_dat|=0x01;
else
read_dat&=0xfe;
Rom_sckH;
}
return(read_dat);
}
// Send address sub-pro (STM8,STM32,51)
void SPI_Address(unsigned char AddH,unsigned char AddM,unsigned char AddL)
{
Send_Byte(AddH);
Send_Byte(AddM);
Send_Byte(AddL);
}
//客户自己实现,从address地址读取len个字节的数据并存入到pBuff数组当中
unsigned char r_dat_bat(unsigned long address,unsigned long DataLen,unsigned char *pBuff)
{
unsigned long i;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
addrHigh=address>>16;
addrMid=address>>8;
addrLow=(unsigned char)address;
Rom_csL; //片选选中字库芯片
Send_Byte(0x03); //普通读取首先送0X03,然后发送地址高八位addrHigh,中八位addrMid,低八位addrLow。
SPI_Address(addrHigh,addrMid,addrLow);
for(i=0;i<DataLen;i++)
*(pBuff+i)=Get_Byte();
Rom_csH;
return 0;
}
//客户自己实现,从address地址读取一个字节的数据并返回该数据
unsigned char r_dat(u32 address){
unsigned char buff;
unsigned char addrHigh;
unsigned char addrMid;
unsigned char addrLow;
addrHigh=address>>16;
addrMid=address>>8;
addrLow=(unsigned char)address;
Rom_csL;
Send_Byte(0x03);
SPI_Address(addrHigh,addrMid,addrLow);
buff = Get_Byte();
Rom_csH;
return buff;
}
/******************************************************
客户自己实现, 库文件函数内部需要调用该函数匹配芯片ID号
根据说明文件或头文件是否需要, 没有就不需要实现
******************************************************/
unsigned char CheckID(unsigned char CMD, unsigned long address,
unsigned long byte_long,unsigned char *p_arr)
{
unsigned long j;
Rom_csL;
Send_Byte(CMD);
Send_Byte((unsigned char)((address)>>16));
Send_Byte((unsigned char)((address)>>8));
Send_Byte((unsigned char)address);
for(j=0;j<byte_long;j++)
{
p_arr[j]=Get_Byte();
}
Rom_csH;
return 1;
}
4.3 gt_read_data
/**
* @brief 发送读取函数
*
* @param sendbuf 发送数据的buff
* @param sendlen 发送数据长度
* @param receivebuf 读取数据的buff
* @param receivelen 读取数据长度
*/
unsigned char gt_read_data(unsigned char * sendbuf, unsigned char sendlen, unsigned char * receivebuf, unsigned int receivelen)
{
unsigned int i;
Rom_csL; //拉低片选cs,选中flash
for(i = 0; i < sendlen; i++)
{
SPI1_ReadWriteByte(sendbuf[i]); //发送数据
}
for(i = 0; i < receivelen; i++)
{
receivebuf[i] = SPI1_ReadWriteByte(0x00); //接收数据,保存到receivebuf[]
}
Rom_csH; //拉高片选cs,空间
return 1;
}
//注:如何验证gt_read_data()是正常工作的?
//使用如下代码验证,看tmp_buf里面的数据。如果tmp_buf里面的数据不是0或0xff, 而是有规律的一组数字,前三位的最后一位在12-19范围内或者是C9,
//则是大概率是正确的。数据正确后并实现了r_dat_bat()函数,r_dat_bat()实现方式请参考本文件夹其余3个文档,根据自己的情况选其中一个实现
//即可则可以尝试在SPI初始化后加入GT_Font_Init函数,如果GT_Font_Init返回值大于0则表示字库初始化成功。
uint8_t tmp_buf[8] = {0};
tmp_buf[0] = 0x9F;
gt_read_data(tmp_buf , 1 , tmp_buf , 8);
for(int i =0;i<8;i++)
{
printf("tmp_buf:%x\r\n",tmp_buf[i]);
}

