高通字库
版本 V1.0 · 更新于 2026-05-23

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]);
  }