全志平台速鼎模块的BT调试记录

1、电路原理:

2、需要配置的接口

控制管脚:
AP_WAKE_BT<---->PL06
BT_LDO_EN<----->PL07
BT_POWER <------>VCC_CTP(CLDO1-3.3V)
通信管脚:
UART1_TXD<----->PG06
UART1_RXD<----->PG07
UART1_RTS<----->PG08
UART1_CTS<----->PG09
PCM蓝牙音频通路管脚(对应I2S1,需要配置成PCM模式):
PCM_SYNC<------->PG10
PCM_BCLK<------->PG11
PCM_DO  <------->PG12
PCO_DI  <------->PG13
32K_OUT <------->PG14

3、上电时序,需要对控制空间和串口流控进行操作,具体如下:

lichee\linux-4.9\drivers\misc\sunxi-rf\sunxi-bluetooth.c
//以下代码是linux4.9 android8.x的代码,满足开机打开电源,上层Open时打开使能,close时关闭使能。
struct sunxi_bt_platdata {
	struct regulator *bt_power;
	struct regulator *io_regulator;
	struct clk *lpo;
	int gpio_bt_rst;
	char *bt_power_name;
	char *io_regulator_name;

	int power_state;
	int uart_enable;
	struct rfkill *rfkill;
	struct platform_device *pdev;
};
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/pm_runtime.h>

#include "sunxi-rfkill.h"

static struct sunxi_bt_platdata *bluetooth_data = NULL;
static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off);
static DEFINE_MUTEX(sunxi_bluetooth_mutex);

static int set_bt_uart_enable(struct sunxi_bt_platdata *platdata, int enable)
{
	int gpio_tx = GPIOG(6);
	int gpio_rx = GPIOG(7);
	int UART_FUNC = 2;
	char pin_name_tx[SUNXI_PIN_NAME_MAX_LEN];
	char pin_name_rx[SUNXI_PIN_NAME_MAX_LEN];
	long unsigned int config_set;
	long unsigned int config_get;

	sunxi_gpio_to_name(gpio_tx, pin_name_tx);
	sunxi_gpio_to_name(gpio_rx, pin_name_rx);

	if (0 == enable) {

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
				SUNXI_PIN_OUTPUT_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
				SUNXI_PIN_DATA_LOW);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC,
				SUNXI_PIN_OUTPUT_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_OUTPUT_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output func failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output func success\n",
					pin_name_rx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,
				SUNXI_PIN_DATA_LOW);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_DATA_LOW != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set output LOW failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set output LOW success\n",
					pin_name_rx);
		}

	} else if (1 == enable) {

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
				SUNXI_PIN_PULL_UP);
		pin_config_set(SUNXI_PINCTRL, pin_name_tx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_tx, &config_get);
		if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
					pin_name_tx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
					pin_name_tx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, UART_FUNC);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_FUNC, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (UART_FUNC != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart func failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart func success\n",
					pin_name_rx);
		}

		config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD,
				SUNXI_PIN_PULL_UP);
		pin_config_set(SUNXI_PINCTRL, pin_name_rx, config_set);
		config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
		pin_config_get(SUNXI_PINCTRL, pin_name_rx, &config_get);
		if (SUNXI_PIN_PULL_UP != SUNXI_PINCFG_UNPACK_VALUE(config_get)) {
			dev_err(&platdata->pdev->dev, "[%s] set uart pull failed\n",
					pin_name_rx);
			return -EINVAL;
		} else {
			dev_info(&platdata->pdev->dev, "[%s] set uart pull success\n",
					pin_name_rx);
		}
	}
	platdata->uart_enable = enable;

	return 0;
}

static int sunxi_bt_on(struct sunxi_bt_platdata *data, bool on_off)
{
	struct platform_device *pdev = data->pdev;
	struct device *dev = &pdev->dev;
	int ret = 0;
	pr_err("%s ---sunxi_bt_on()\n", __FUNCTION__);
#if 0
	if (!on_off && gpio_is_valid(data->gpio_bt_rst))
		gpio_set_value(data->gpio_bt_rst, 0);
#endif

	if (data->bt_power_name) { //bt_power
		data->bt_power = regulator_get(dev, data->bt_power_name);
		if (!IS_ERR(data->bt_power)) {
			if (on_off) {
				regulator_set_voltage(data->bt_power,
						(int)3300*1000,
						(int)3300*1000);

				ret = regulator_enable(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power enable failed\n");
					regulator_put(data->bt_power);
					return ret;
				}

				ret = regulator_get_voltage(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power get voltage failed\n");
					regulator_put(data->bt_power);
					return ret;
				}
				dev_info(dev, "check bluetooth bt_power voltage: %d\n", ret);
			} else {
				ret = regulator_disable(data->bt_power);
				if (ret < 0) {
					dev_err(dev, "regulator bt_power disable failed\n");
					regulator_put(data->bt_power);
					return ret;
				}
			}
			regulator_put(data->bt_power);
		}
	}

#if 0
	if(data->io_regulator_name){
		data->io_regulator = regulator_get(dev, data->io_regulator_name);
		if (!IS_ERR(data->io_regulator)) {
			if (on_off) {
				regulator_set_voltage(data->io_regulator,
						(int)3300*1000,
						(int)3300*1000);

				ret = regulator_enable(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator enable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}

				ret = regulator_get_voltage(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator get voltage failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
				dev_info(dev, "check bluetooth io_regulator voltage: %d\n",ret);
			}else{
				ret = regulator_disable(data->io_regulator);
				if (ret < 0) {
					dev_err(dev, "regulator io_regulator disable failed\n");
					regulator_put(data->io_regulator);
					return ret;
				}
			}
			regulator_put(data->io_regulator);
		}
	}

	if (on_off && gpio_is_valid(data->gpio_bt_rst)) {
		mdelay(10);
		gpio_set_value(data->gpio_bt_rst, 1);
	}
#endif
	data->power_state = on_off;

	return 0;
}

static int sunxi_bt_set_block(void *data, bool blocked)
{
	struct sunxi_bt_platdata *platdata = data;
	struct platform_device *pdev = platdata->pdev;
	int ret;

	if (blocked != platdata->power_state) {
		dev_warn(&pdev->dev, "block state already is %d\n", blocked);
		return 0;
	}

	dev_info(&pdev->dev, "set block: %d\n", blocked);
	//ret = sunxi_bt_on(platdata, !blocked);
	//if (ret) {
	//	dev_err(&pdev->dev, "set block failed\n");
	//	return ret;
	//}

	//sunxi_wl_chipen_set(1, !blocked);

	return 0;
}

static const struct rfkill_ops sunxi_bt_rfkill_ops = {
	.set_block = sunxi_bt_set_block,
};

static ssize_t power_state_show(struct class *class,
		struct class_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", bluetooth_data->power_state);
}

void bt_xr829_enable(int enable);
//主要由这个函数在上层调用/sys/class/bt_power/power_state接口,1:使能 0:不使能
static ssize_t power_state_store(struct class *class,
		struct class_attribute *attr, const char *buf, size_t count)
{
	unsigned long state;
	int err;
	
	pr_err("%s ---power_state_store()\n", __FUNCTION__);
	err = kstrtoul(buf, 0, &state);
	if (err)
		return err;
    	bluetooth_data->power_state = state;
	pr_err("%s ---bt_xr829_enable = %ld\n",__FUNCTION__,state);
	if(state)
		bt_xr829_enable(1);
	else
		bt_xr829_enable(0);
	return count;

#if 0
	if (state > 1)
		return count;

	mutex_lock(&sunxi_bluetooth_mutex);
	if (state != bluetooth_data->power_state) {
		dev_info(&bluetooth_data->pdev->dev, "set power: %s\n", state ? "on" : "off");
		err = sunxi_bt_on(bluetooth_data, state);
		if (err) {
			dev_err(&bluetooth_data->pdev->dev, "set power failed\n");
		}
	}
	sunxi_wl_chipen_set(1, state);
	mutex_unlock(&sunxi_bluetooth_mutex);

	return count;
#endif
}

static ssize_t uart_enable_store(struct class *class,
		struct class_attribute *attr, const char *buf, size_t count)
{
	unsigned long enable;
	int err;

	err = kstrtoul(buf, 0, &enable);
	if (err)
		return err;

	if (enable > 1)
		return count;
	
	pr_err("%s ---enable:%d\n", __FUNCTION__,enable);
	if (enable != bluetooth_data->uart_enable) {
		dev_info(&bluetooth_data->pdev->dev, "bt uart: %s\n", enable ? "on" : "off");
		err = set_bt_uart_enable(bluetooth_data, enable);
		if(0==err)
		{
		  if (0 == enable){
		  	bluetooth_data->uart_enable = 0;
		  } else {
				bluetooth_data->uart_enable = 1;
		  }
	   }
	   else{
			dev_err(&bluetooth_data->pdev->dev, "set uart failed\n");
		}
	}

	return count;
}

static struct class_attribute bt_attribute_group[] = {
	__ATTR(power_state, S_IRUGO | S_IWUSR | S_IWGRP,
			power_state_show, power_state_store),
	__ATTR(uart_enable, S_IWUSR | S_IWGRP,
			NULL, uart_enable_store),
	__ATTR_NULL
};

static struct class bt_power_class = {
	.name = "bt_power",
	.class_attrs = bt_attribute_group,
};
static int bt_xr829_reset = 0;
static int bt_xr829_wake = 0;
void bt_xr829_enable(int enable)
{
	pr_err("%s aa enable=%d\n",__FUNCTION__,enable);
	if(enable){
	gpio_direction_output(bt_xr829_reset, 1); //AP_WAKE_BT<---->PL06和BT_LDO_EN<----->PL07同时打开
	gpio_direction_output(bt_xr829_wake, 1);
	}else{
			gpio_direction_output(bt_xr829_wake, 0);
			gpio_direction_output(bt_xr829_reset, 0);
	}
}

static int sunxi_bt_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct device *dev = &pdev->dev;
	struct sunxi_bt_platdata *data;
	struct gpio_config config;
	const char *power, *io_regulator;
	int ret = 0;
    dev_info(dev, "sunxi_bt_probe()\n");
	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;

	data->pdev = pdev;
	bluetooth_data = data;

	if (of_property_read_string(np, "bt_power", &power)) {
		dev_warn(dev, "Missing bt_power.\n");
	} else {
		data->bt_power_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->bt_power_name)
			return -ENOMEM;
		else
			strcpy(data->bt_power_name,power);
	}
	dev_info(dev, "bt_power_name (%s)\n", data->bt_power_name);
	bt_xr829_reset = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(bt_xr829_reset)) {
		pr_err("get gpio bt_rst failed\n");
	} else {
		pr_err("bt_rst gpio=%d gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",bt_xr829_reset,
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, bt_xr829_reset, "bt_xr829_reset");
		if (ret < 0) {
			dev_err(dev, "can't request bt_rst gpio %d\n",
				bt_xr829_reset);
			return ret;
		}

		ret = gpio_direction_output(bt_xr829_reset, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_rst gpio %d\n",
				bt_xr829_reset);
			return ret;
		}
	}

	bt_xr829_wake = of_get_named_gpio_flags(np, "ap_wakeup_bt", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(bt_xr829_wake)) {
		pr_err("get gpio bt_xr829_wake failed\n");
	} else {
		pr_err("bt_xr829_wake gpio=%d gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",bt_xr829_wake,
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, bt_xr829_wake, "ap_wakeup_bt");
		if (ret < 0) {
			dev_err(dev, "can't request bt_xr829_wake gpio %d\n",
				bt_xr829_wake);
			return ret;
		}

		ret = gpio_direction_output(bt_xr829_wake, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_xr829_wake %d\n",
				bt_xr829_wake);
			return ret;
		}
	}

#if 0
	if (of_property_read_string(np, "bt_io_regulator", &io_regulator)) {
		dev_warn(dev, "Missing bt_io_regulator.\n");
	} else {
		data->io_regulator_name = devm_kzalloc(dev, 64, GFP_KERNEL);
		if (!data->io_regulator_name)
			return -ENOMEM;
		else
			strcpy(data->io_regulator_name,io_regulator);
	}
	dev_info(dev, "io_regulator_name (%s)\n", data->io_regulator_name);

	data->gpio_bt_rst = of_get_named_gpio_flags(np, "bt_rst_n", 0, (enum of_gpio_flags *)&config);
	if (!gpio_is_valid(data->gpio_bt_rst)) {
		dev_err(dev, "get gpio bt_rst failed\n");
	} else {
		dev_info(dev, "bt_rst gpio=%d  mul-sel=%d  pull=%d  drv_level=%d  data=%d\n",
				config.gpio,
				config.mul_sel,
				config.pull,
				config.drv_level,
				config.data);

		ret = devm_gpio_request(dev, data->gpio_bt_rst, "bt_rst");
		if (ret < 0) {
			dev_err(dev, "can't request bt_rst gpio %d\n",
				data->gpio_bt_rst);
			return ret;
		}

		ret = gpio_direction_output(data->gpio_bt_rst, 0);
		if (ret < 0) {
			dev_err(dev, "can't request output direction bt_rst gpio %d\n",
				data->gpio_bt_rst);
			return ret;
		}
	}

	data->lpo = devm_clk_get(dev, NULL);
	if (IS_ERR_OR_NULL(data->lpo)) {
		dev_warn(dev, "clk not config\n");
	} else {
		ret = clk_prepare_enable(data->lpo);
		if (ret < 0)
			dev_warn(dev, "can't enable clk\n");
	}
#endif

	data->rfkill = rfkill_alloc("sunxi-bt", dev, RFKILL_TYPE_BLUETOOTH,
				&sunxi_bt_rfkill_ops, data);
	if (!data->rfkill) {
		ret = -ENOMEM;
		goto failed_alloc;
	}

	rfkill_set_states(data->rfkill, true, false);

	ret = rfkill_register(data->rfkill);
	if (ret){
		goto fail_rfkill;
	}
	platform_set_drvdata(pdev, data);

	class_register(&bt_power_class);

	data->power_state = 0; //power_state为0,不使能
	data->uart_enable = 1; //UART口OK,状态为1
    //电源管理
	pm_runtime_set_active(dev); 
	pm_runtime_get(dev);
	pm_runtime_enable(dev);

	if (1 != data->power_state) {
		sunxi_bt_on(data, 1); //打开模块电源
	}

	return 0;

fail_rfkill:
	if (data->rfkill) 
		rfkill_destroy(data->rfkill);
failed_alloc:
	//if (!IS_ERR_OR_NULL(data->lpo)) {
	//	clk_disable_unprepare(data->lpo);
	//	clk_put(data->lpo);
	//}
	return ret;
}

static int sunxi_bt_remove(struct platform_device *pdev)
{
	struct sunxi_bt_platdata *data = platform_get_drvdata(pdev);
	struct rfkill *rfk = data->rfkill;

	pm_runtime_disable(&pdev->dev);
	pm_runtime_set_suspended(&pdev->dev);

	class_unregister(&bt_power_class);

	platform_set_drvdata(pdev, NULL);
	
	if(rfk){
		rfkill_unregister(rfk);
		rfkill_destroy(rfk);
	}

	//if (!IS_ERR_OR_NULL(data->lpo)) {
	//	clk_disable_unprepare(data->lpo);
	//	clk_put(data->lpo);
	//}

	return 0;
}

static int bt_pm_suspend(struct device *dev)
{
	struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter bt_pm_suspend.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	if (platdata) {
		if (0 != platdata->power_state) {
			sunxi_bt_on(platdata, 0);
		}
	}

	return 0;
}

static int bt_pm_resume(struct device *dev)
{
	struct sunxi_bt_platdata *platdata = dev_get_drvdata(dev);

	printk("CONFIG_PM:enter bt_pm_resume.\n");

	if (pm_runtime_suspended(dev))
		return 0;

	if (platdata) {
		if (1 != platdata->power_state) {
			sunxi_bt_on(platdata, 1);
		}
	}

	return 0;
}
static struct dev_pm_ops bt_pm_ops = {
	.suspend = bt_pm_suspend,
	.resume  = bt_pm_resume,
};

static const struct of_device_id sunxi_bt_ids[] = {
	{ .compatible = "allwinner,sunxi-bt" },
	{ /* Sentinel */ }
};

static struct platform_driver sunxi_bt_driver = {
	.probe	= sunxi_bt_probe,
	.remove	= sunxi_bt_remove,
	.driver	= {
		.owner	= THIS_MODULE,
		.name	= "sunxi-bt",
		.pm     = &bt_pm_ops,
		.of_match_table	= sunxi_bt_ids,
	},
};

module_platform_driver(sunxi_bt_driver);

MODULE_DESCRIPTION("sunxi bluetooth driver");
MODULE_LICENSE(GPL);

//BT的管脚系统配置
tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
;--------------------------------------------------------------------------------
;bluetooth configuration
;bt_used:           0- no used, 1- used
;clocks:            external low power clock input (32.768KHz)
;bt_power:          input supply voltage
;bt_io_regulator:   bluetooth I/O voltage
;bt_rst_n:          power up/down internal regulators used by BT section
;--------------------------------------------------------------------------------
[bt]
bt_used             = 1
compatible = "allwinner,sunxi-bt"
bt_power            = "vcc-ctp" //cldo1 vcc-ctp
;clocks             = 0x55
bt_rst_n            = port:PL07<1><default><default><0> //BT_LDO_EN<----->PL07
ap_wakeup_bt        = port:PL06<1><default><1><0> //AP_WAKE_BT<---->PL06

4、UART口和PCM通道的系统管脚和模式配置:

lichee\tools\pack\chips\sun8iw17p1\configs\k23x3-wb\sys_config.fex
...............
[uart1]
uart1_used       = 1  //这里必须设置成1
uart1_port       = 1
uart1_type       = 2
uart1_tx         = port:PG06<2><1><default><default>
uart1_rx         = port:PG07<2><1><default><default>
uart1_rts        = port:PG08<2><1><default><default>
uart1_cts        = port:PG09<2><1><default><default>
[uart1_suspend]
uart1_tx         = port:PG06<7><1><default><default>
uart1_rx         = port:PG07<7><1><default><default>
uart1_rts        = port:PG08<7><1><default><default>
uart1_cts        = port:PG09<7><1><default><default>
..............
;--------------------------------------------------------------------------------
;		NOTE :Make sure daudio1mach_used = 0x1,daudio1_used = 0x1,
;         if register the sound card DAUDIO1.
;--------------------------------------------------------------------------------
;--------------------------------------------------------------------------------
;allwinner,pcm_lrck_period 	:16/32/64/128/256
;allwinner,pcm_lrckr_period :no use
;allwinner,slot_width_select 	:16bits/20bits/24bits/32bits
;allwinner,pcm_lsb_first 	:0: msb first; 1: lsb first
;allwinner,tx_data_mode 	:0: 16bit linear PCM; 1: 8bit linear PCM; 2: 8bit u-law; 3: 8bit a-law
;allwinner,rx_data_mode 	:0: 16bit linear PCM; 1: 8bit linear PCM; 2: 8bit u-law; 3: 8bit a-law
;allwinner,daudio_master :1: SND_SOC_DAIFMT_CBM_CFM(codec clk & FRM master)        use
;						  2: SND_SOC_DAIFMT_CBS_CFM(codec clk slave & FRM master)  not use
;						  3: SND_SOC_DAIFMT_CBM_CFS(codec clk master & frame slave) not use
;						  4: SND_SOC_DAIFMT_CBS_CFS(codec clk & FRM slave)         use
;allwinner,audio_format: 1:SND_SOC_DAIFMT_I2S(standard i2s format).            use
;			   2:SND_SOC_DAIFMT_RIGHT_J(right justfied format).
;			   3:SND_SOC_DAIFMT_LEFT_J(left justfied format)
;			   4:SND_SOC_DAIFMT_DSP_A(pcm. MSB is available on 2nd BCLK rising edge after LRC rising edge). use
;			   5:SND_SOC_DAIFMT_DSP_B(pcm. MSB is available on 1nd BCLK rising edge after LRC rising edge)
;allwinner,signal_inversion:1:SND_SOC_DAIFMT_NB_NF(normal bit clock + frame)  use
;				  2:SND_SOC_DAIFMT_NB_IF(normal BCLK + inv FRM)
;				  3:SND_SOC_DAIFMT_IB_NF(invert BCLK + nor FRM)  use
;				  4:SND_SOC_DAIFMT_IB_IF(invert BCLK + FRM)
;allwinner,frametype :0: short frame = 1 clock width;  1: long frame = 2 clock width
;allwinner,tdm_config :0:pcm 1:i2s
;allwinner,mclk_div :0: not output(normal setting this) 1/2/4/6/8/12/16/24/32/48/64/96/128/176/192:
;			setting mclk as input clock to external codec, freq is pll_audio/mclk_div
;allwinner,daudio0_used :0:not use 1:use
;-------------------------------------------------------------------------------
;		NOTE :Make sure daudio0mach_used = 0x1,daudio0_used = 0x1,
;         if register the sound card DAUDIO0.
;--------------------------------------------------------------------------------
[snddaudio1]
snddaudio1_used = 1
;-----------------------------------------------------------------------------
[daudio1]
;pcm_lrck_period =   0x20
pcm_lrck_period =   0x40
pcm_lrckr_period =   0x01
slot_width_select =   0x10
pcm_lsb_first =   0x0
tx_data_mode =   0x0
rx_data_mode =   0x0
;daudio_master =   0x04
;audio_format =   0x01
;signal_inversion =   0x01
daudio_master =   0x01
audio_format =   0x04
signal_inversion =   0x02
frametype =   0x0
;tdm_config =   0x01
tdm_config =   0x00
mclk_div = 0x0
;daudio1_used = 0
daudio1_used = 1  //这里一定要配置成1
//以上参数请参考BT模组中PCM输出的时序

5、修改上层访问的设备节点的权限和添加固件

android\device\softwinner\common\init.wireless.qcom.rc
在文件中添加:
......................
# to observe dnsmasq.leases file for dhcp information of soft ap.
    chown dhcp system /data/misc/dhcp
    chmod 0666 /sys/class/bt_power/power_state //添加权限
android\device\softwinner\t7-common\ueventd.sun8iw17p1.rc
............................
/dev/ttyS*                  0666    system      system
我们使用UART1接口连接蓝牙,使用/dev/ttyS1设备节点

添加固件:
需要将以下固件文件拷贝到/system/etc/firmware/目录中
android\device\softwinner\k23x3-wb\hardware\bt\firmware\nvm_tlv_3.2.bin
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_3.2.tlv
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_3.2.tlv
android\device\softwinner\k23x3-wb\hardware\bt\firmware\rampatch_tlv_tf_1.1.tlv
修改:
android\device\softwinner\k23x3-wb\k23x3_wb.mk
# BT
PRODUCT_COPY_FILES += \    $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/nvm_tlv_3.2.bin:/system/etc/firmware/nvm_tlv_3.2.bin \    $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/rampatch_tlv_3.2.tlv:/system/etc/firmware/rampatch_tlv_3.2.tlv \    $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/nvm_tlv_tf_1.1.bin:/system/etc/firmware/nvm_tlv_tf_1.1.bin \    $(PRODUCT_DEVICE_PATH)/hardware/bt/firmware/rampatch_tlv_tf_1.1.tlv:/system/etc/firmware/rampatch_tlv_tf_1.1.tlv \

以上的信息配置OK,之后。直接找模组供应商,要协议栈的相关程序,因为模组用于车载项目,不能使用Android自带的framework层部分,直接使用BT模块的CHI模式,不是CMD模式,如果使用CMD模式,直接跟模组发AT指令即可,如果使用chi模式,需要模组厂家或第第三方给出协议栈的相关文件,具体跟APP有关,到这个步骤,OS层面就OK了。而通过跟速鼎调试来看,速鼎将提供:

system/bin/gocsdk
system/etc/firmware/nvm_tlv_tf_1.1.bin
system/etc/firmware/rampatch_tlv_tf_1.1.tlv
system/config_具体公司.ini //不同厂家内容可能不同

其中system/bin/gocsdk文件将在系统启动的时候用服务进行加载,将使能 BT模块,打开/dev/ttyS1,打开PCM通道,同时处理蓝牙连接,播放音乐,打电话,同步电话本等操作,主要是协议栈的处理。

系统启动加载的蓝牙服务
android\device\softwinner\k23x3-wb\init.device.rc
service gocsdk /system/bin/gocsdk /dev/ttyS1 500000 //这个主要是供应商给的协议栈等
        class main
        user root
        group root

service gocsdk_be130 /system/bin/gocsdk_be130 /dev/ttyS1 500000
        class main
        user root
        group root
        disabled

将以上文件导入到系统中对应目录之后,重启系统,然后基本就可以连接手机,打电话、播放音乐了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值