XFS5152CE是科大讯飞的TTS语音合成芯片,提供UART,SPI,I2C三种接口为用户实现TTS语音数据的传输播报。下面以STM32为主设备用I2C接口方式进行语音芯片的驱动,XFS5152CE作为从设备,接收主设备发过来的播报数据帧进行处理播报输出。
由官方提供的芯片用户手册,我们可以参考器电路原理进行硬件连接,参考其i2c接口提示,进行软件驱动的编写与调试。
由于说STM32的片上外设i2c存在一定缺陷,而且XFS5152CE芯片的i2c通信速率比较慢(硬件i2c通常速率较高),这里STM32F103我使用的是GPIO模拟I2C的方案,速度也比较好控制,即采用延时函数实现通信时钟SCL的控制。
user_i2c.h
#ifndef __USER_IIC_H__
#define __USER_IIC_H__
#include "stm32f10x.h"
#include "stm32f10x_i2c.h"
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
//IO方向设置
#define SDA_IN() {GPIOD->CRH&=0X0FFFFFFF;GPIOD->CRH|=8<<28;}
#define SDA_OUT() {GPIOD->CRH&=0X0FFFFFFF;GPIOD->CRH|=3<<28;}
//IO操作函数
#define IIC_SCL PDout(14) //SCL
#define IIC_SDA PDout(15) //SDA
#define READ_SDA PDin(15) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte();//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void XFS5152CE_IIC_WriteBytes(unsigned char* buf, int len);
u8 XFS5152CE_IIC_ReadOneByte(u8 addr);
void TTStest(void);
#endif
user_i2c.c
/*
本文件为STM32正对于科大讯飞的语音合成芯片XFS5152CE进行I2C驱动实现语音的播报驱动
为保证通讯稳定性,XFS5152CE的i2c接口通信速度要求不高于15KHZ
本示例使用io模拟i2c实现对XFS5152CE的数据写读
*/
#include "user_i2c.h"
#include "user_delay.h"
#include "user_usart1.h"
#define I2C_Speed 20 //us
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_Init(GPIOD, &GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
DELAY_Us(I2C_Speed);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
DELAY_Us(I2C_Speed);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
DELAY_Us(I2C_Speed);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
DELAY_Us(I2C_Speed);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;DELAY_Us(I2C_Speed);
IIC_SCL=1;DELAY_Us(I2C_Speed);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>I2C_Speed)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
DELAY_Us(I2C_Speed);
IIC_SCL=1;
DELAY_Us(I2C_Speed);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
DELAY_Us(I2C_Speed);
IIC_SCL=1;
DELAY_Us(I2C_Speed);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
DELAY_Us(I2C_Speed);
IIC_SCL=1;
DELAY_Us(I2C_Speed); //对TEA5767这三个延时都是必须的
IIC_SCL=0;
DELAY_Us(I2C_Speed);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
DELAY_Us(I2C_Speed);
IIC_SCL=1;
receive<<=1;
if(READ_SDA) receive++;
DELAY_Us(I2C_Speed);
}
IIC_Ack(); //发送ACK
return receive;
}
void XFS5152CE_IIC_WriteBytes(unsigned char* buf, int len)
{
int i;
unsigned char t,ack;
IIC_Start();
IIC_Send_Byte(0x80);
IIC_Wait_Ack();
for (i=0; i<len; i++)
{
IIC_Send_Byte(buf[i]);
IIC_Wait_Ack();
IIC_SCL=0;
DELAY_Us(I2C_Speed);
}
// ack = XFS5152CE_IIC_ReadOneByte(addr);
IIC_Stop();
}
u8 XFS5152CE_IIC_ReadOneByte(u8 addr)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
temp=IIC_Read_Byte();
IIC_Stop();//产生一个停止条件
u1_printf("temp read:0x%02x\n",temp);
return temp;
}
void TTStest(void)
{
int i = 0;
unsigned char testBuf[13]={0xFD, 0x00, 0x0A, 0x01, 0x00, 0xBF,
0xC6, 0xB4, 0xF3, 0xD1, 0xB6, 0xB7, 0xC9};
unsigned char volumeSet[7]={0xFD, 0x00, 0x04, 0x41, 0x01, 0x05, 0x01};
XFS5152CE_IIC_WriteBytes(testBuf,13);
u1_printf("tts test\n");
}
调试时,特别注意GPIO口模式配置,这里参考的是正点原子的STM32模拟i2c驱动进行修改实现的,调试过程中,使用到的示波器进行SCL和SDA两根数据线的时序进行分析判断,在GPIO控制输入输出寄存器的配置处,卡了好久,最终还是配置正确了,下面是SDA采用GPIOD_Pin15的输入输出寄存器配置。
#define SDA_IN() {GPIOD->CRH&=0X0FFFFFFF;GPIOD->CRH|=8<<28;}
#define SDA_OUT() {GPIOD->CRH&=0X0FFFFFFF;GPIOD->CRH|=3<<28;}
配置寄存器之前,需要对这个寄存器进行查找其功能含义和配置工作方式:
查看STM32数据手册:
因为用到的是pin15,所以是GPIOx_CRH,如果用到时pin0~7,则需找GPIOx_CRL进行配置;MODE15的两位控制着GPIOD_Pin15的输入输出模式,通过对这两位进行位操作即可设置其输入输出工作模式。
参考I 2 C-bus specification and user manual文档,对I2C有一个较全面的了解,通信原理和收发过程,对上面代码的理解很有帮助。