讯飞XFS5152 语音模块在RK3288 上的应用

公司产品使用XFS5152语音模块作为语音提示应用在RK3288 平台上,这里记录一下驱动调试过程。

XFS5152 支持 UART、I2C 、SPI 三种通讯方式,将收到的中文、英文文本进行语音合成。

产品中RK3288 使用I2C连接该模块,但存在一个问题该模块只支持低速率的I2C,速度最大只能到15KHz,

但RK3288 支持的标准I2C速率为100KHz,实际测试发现虽然可以设置到10KHZ,

但波形发生改变,只能使用GPIO模拟的方式与模块通信,Linux内核已支持该功能。

设备树配置如下:

i2c-gpio {
		compatible = "i2c‐gpio";
		gpios = <&gpio6 9 GPIO_ACTIVE_HIGH>,
			<&gpio6 10 GPIO_ACTIVE_HIGH>;
		/* i2c‐gpio,delay‐us = <5>; */
		#address-cells = <1>;
		#size-cells = <0>;
		pinctrl-names = "default";
		pinctrl-0 = <&i2c6_gpio>;
		status = "okay";

	        xfs5152: xfs5152@40 {
	    		status = "okay";
		    	compatible = "XFS5152";
		    	reg = <0x40>;
		    	stat-gpio = <&gpio6 8 GPIO_ACTIVE_LOW>;
		    	rst-gpio = <&gpio6 0 GPIO_ACTIVE_LOW>;
	        };
	};
}

    i2c6 {
		i2c6_gpio: i2c6-gpio {
			rockchip,pins = <6 9 RK_FUNC_GPIO &pcfg_pull_up>,
					<6 10 RK_FUNC_GPIO &pcfg_pull_up>;
		};	
	};

注意如果复用硬件I2C引脚需要将原i2c节点disable !!

在内核中使能i2c-gpio 驱动:

 

编译内核烧录至板卡中查看驱动是否加载成功:

 

I2c-gpio驱动没有问题,开始编写XFS5152 的驱动代码,由于公司保密限制,只贴出验证用驱动代码:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define XFS5152_CNT	1
#define XFS5152_NAME	"xfs5152"

struct xfs5152_dev {
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	struct device_node	*nd; /* 设备节点 */
    	int pwr_gpio,rst_gpio,stat_gpio;
	int major;			/* 主设备号 */
	void *private_data;	/* 私有数据 */

};

static struct xfs5152_dev xfs5152;

static int xfs5152_gpio_init(void)
{

	printk("now xfs5152_gpio_init() is called\r\n");
	xfs5152.nd = of_find_node_by_path("/i2c-gpio/xfs5152");
	if (xfs5152.nd== NULL) {
		return -EINVAL;
	}
	/*
	xfs5152.pwr_gpio = of_get_named_gpio(xfs5152.nd ,"pwr-gpio", 0);
	if (xfs5152.pwr_gpio < 0) {
		printk("can't get pwr_gpio0\r\n");
		return -EINVAL;
	}
	printk("pwr_gpio=%d\r\n", xfs5152.pwr_gpio);
	*/

	xfs5152.stat_gpio = of_get_named_gpio(xfs5152.nd ,"stat-gpio", 0);
	if (xfs5152.pwr_gpio < 0) {
		printk("can't get stat_gpio0\r\n");
		return -EINVAL;
	}
	printk("stat_gpio=%d\r\n", xfs5152.stat_gpio);

    	xfs5152.rst_gpio = of_get_named_gpio(xfs5152.nd ,"rst-gpio", 0);
	if (xfs5152.pwr_gpio < 0) {
		printk("can't get rst_gpio0\r\n");
		return -EINVAL;
	}
	printk("rst_gpio=%d\r\n", xfs5152.rst_gpio);
	
	/* 初始化key所使用的IO */
	#if 0
	gpio_request(xfs5152.pwr_gpio, "pwr_gpio0");	/* 请求IO */
	gpio_direction_output(xfs5152.pwr_gpio,1);	/* 设置为输入 */
	#endif
	gpio_request(xfs5152.rst_gpio, "rst_gpio0");	/* 请求IO */
	gpio_direction_output(xfs5152.rst_gpio,1);	/* 设置为输入 */

    	gpio_request(xfs5152.stat_gpio, "stat_gpio0");	/* 请求IO */
	gpio_direction_input(xfs5152.stat_gpio);	/* 设置为输入 */

	return 0;
}    


static int xfs5152_reset(void)
{
	int ret = 0;

	if (gpio_get_value(xfs5152.stat_gpio) != 0) {  
		/* 申请复位IO,并且默认输出低电平 */
		printk("is busy then reset xfs5152 \r\n");
	    	gpio_set_value(xfs5152.rst_gpio, 0);	/* 输出低电平,复位 */
		msleep(100);

		printk("reset xfs5152 ----end \r\n");
		gpio_set_value(xfs5152.rst_gpio, 1);	/* 输出高电平,停止复位 */
	
	}

    	ret = gpio_get_value(xfs5152.stat_gpio);
	if(ret==0){
		printk("after reset is not busy \r\n");		
	}
	else{
		printk("after reset is  busy NOW \r\n");		
	}

	return ret;
}


/*
 * @description	: 向xfs5152_dev多个寄存器写入数据
 * @param - dev:  xfs5152_dev设备
 * @param - reg:  要写入的寄存器首地址
 * @param - val:  要写入的数据缓冲区
 * @param - len:  要写入的数据长度
 * @return 	  :   操作结果
 */
static s32 xfs5152_write_regs(struct xfs5152_dev *dev, u8 *buf, u8 len)
{
	u8 b[15]={0xFD ,0x00 ,0x0C ,0x01 ,0x01 ,0xB1 ,0xB1 ,0xBE ,0xA9 ,0xBB ,0xB6 ,0xD3 ,0xAD ,0xC4 ,0xFA };
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	//memcpy(&b,buf,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = 0x40;	/* xfs5152地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = 15;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}


/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int xfs5152_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &xfs5152;

	return 0;
}
/*
 * @description		: 从设备写数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t xfs5152_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *off)
{
    	int retvalue;
	unsigned char databuf[50];


	struct xfs5152_dev *dev = (struct xfs5152_dev *)filp->private_data;
    
    	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	if(xfs5152_reset()==0)
	{
		printk("The data waitting to send is %s\r\n",databuf);
		xfs5152_write_regs(dev,databuf,cnt);
		printk("The data is sent to iic bus\r\n");

	}
	else{
		printk("xfs5152 now is not ready\r\n");
	}
  	
	return 0;
}


static int xfs5152_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* AP3216C操作函数 */
static const struct file_operations xfs5152_ops = {
	.owner = THIS_MODULE,
	.open = xfs5152_open,
	.write = xfs5152_write,
	.release = xfs5152_release,
};


/*
  * @description     : i2c驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0,成功;其他负值,失败
  */
static int xfs5152_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

    	xfs5152_gpio_init();
	printk("xfs5152_probe function is called\r\n");
        /* 1、构建设备号 */
	if (xfs5152.major) {
		xfs5152.devid = MKDEV(xfs5152.major, 0);
		register_chrdev_region(xfs5152.devid, XFS5152_CNT, XFS5152_NAME);
	} else {
		alloc_chrdev_region(&xfs5152.devid, 0, XFS5152_CNT, XFS5152_NAME);
		xfs5152.major = MAJOR(xfs5152.devid);
	}

	/* 2、注册设备 */
	cdev_init(&xfs5152.cdev, &xfs5152_ops);
	cdev_add(&xfs5152.cdev, xfs5152.devid, XFS5152_CNT);

	/* 3、创建类 */
	xfs5152.class = class_create(THIS_MODULE, XFS5152_NAME);
	if (IS_ERR(xfs5152.class)) {
		return PTR_ERR(xfs5152.class);
	}

	/* 4、创建设备 */
	xfs5152.device = device_create(xfs5152.class, NULL, xfs5152.devid, NULL, XFS5152_NAME);
	if (IS_ERR(xfs5152.device)) {
		return PTR_ERR(xfs5152.device);
	}

    
	xfs5152.private_data = client;


	return 0;
}

/*
 * @description     : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
 * @param - client 	: i2c设备
 * @return          : 0,成功;其他负值,失败
 */
static int xfs5152_remove(struct i2c_client *client)
{
	/* 删除设备 */
	cdev_del(&xfs5152.cdev);
	unregister_chrdev_region(xfs5152.devid, XFS5152_CNT);

	/* 注销掉类和设备 */
	device_destroy(xfs5152.class, xfs5152.devid);
	class_destroy(xfs5152.class);
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id xfs5152_id[] = {
	{"XFS5152", 0},  
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id xfs5152_of_match[] = {
	{ .compatible = "XFS5152" },
	{ /* Sentinel */ }
};

/* i2c驱动结构体 */	
static struct i2c_driver xfs5152_driver = {
	.probe = xfs5152_probe,
	.remove = xfs5152_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "XFS5152",
		   	.of_match_table = xfs5152_of_match, 
		   },
	.id_table = xfs5152_id,
};
		   
/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init xfs5152_init(void)
{
	int ret = 0;
   
	ret = i2c_add_driver(&xfs5152_driver);
	return ret;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit xfs5152_exit(void)
{
	i2c_del_driver(&xfs5152_driver);
}

/* module_i2c_driver(xfs5152_driver) */

module_init(xfs5152_init);
module_exit(xfs5152_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("KuangMH");

测试代码如下:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
 
#define xfs5152 "/dev/xfs5152"
 
int main(void)
{
	unsigned char testBuf[13]={0xFD,  0x00,  0x0A,  0x01,  0x00,  0xBF, 0xC6,  0xB4,  0xF3,  0xD1,  0xB6,  0xB7,  0xC9};//科大讯飞
	int retval;
	int fd = open(xfs5152, O_RDWR);
	
	if (fd < 0) {
	    printf("Open %s failure.\n", xfs5152);
	    return -1;
	}
 
	while(1)
	{
		retval = write(fd, testBuf, 13);
		printf("write to driver %d \r\n",retval);
		write(fd, testBuf, 13);
		sleep(3);
		printf("sending TTS is over!\r\n");
		//memset(buf, 0, 64);
		sleep(3);
	}
	close(fd);
	return 0;	
}

调试波形时序:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值