RISC-V内核中科蓝讯BT8922开发
BT8922
GPIO配置
官方说明部分
GPIOAFEN //0:当作通用GPIO使用 //1:当作其它功能性IO,如串口/SPI..
GPIOADE //数字IO使能: 0为模拟IO, 1 为数字IO, //如作为AUX/MIC输入的IO口就需要设置成模拟IO, 减少数字IO对AUX的干扰.
GPIOADIR //控制IO的方向: 0为输出, 1为输入.
GPIOA //IO方向为输入时,读此寄存器的值即得到引脚的高低状态 //IO为输出时, 设置此寄存器的高低即设置对应引脚的高低输出.
GPIOADRV //0 输出驱动为8mA //1 输出驱动为32 mA //IO方向为输出时有效
//以下寄存器控制上拉或下拉, 注意只有当IO设置为输入时, 上下拉才有效. //IO方向为输出时上下拉自动无效
GPIOAPU //10K上拉使能
GPIOAPD //10K下拉使能
GPIOAPU200K //200K上拉使能
GPIOAPD200K //200K下拉使能
GPIOAPU300 //300欧上拉使能
GPIOAPD300 //300欧下拉使能
//作为输出时,输出电平的高低也可以通过设置GPIOASET/GPIOACLR来快速实现,注意GPIOASET/GPIOACLR只对写1的位有效.
GPIOASET = BIT(0); //PA0输出高, 等效于 GPIOA |= BIT(0);其它位状态不变.
GPIOACLR = BIT(0); //PA0输出低, 等效于GPIOA &= ~BIT(0);其它位状态不变.
常见输入输出使用示例: 1. PA0输出高低(点LED灯)示例:
int main(void)
{
//示例开始
GPIOAFEN &= ~BIT(0); //PA0作为GPIO使用
GPIOADE |= BIT(0); //PA0设置为数字IO
GPIOADIR &= ~BIT(0); //PA0方向设置为输出
WDT_DIS(); //测试程序关看门狗,防止看门狗复位
while(1) {
GPIOA |= BIT(0); //PA0输出高, 等效于GPIOASET = BIT(0);
delay_ms(100);
GPIOA &= ~BIT(0); //PA0输出低, 等效于GPIOACLR = BIT(0);
delay_ms(100);
}
配置得到PA0引脚状态(作用输入)示例:
int main(void)
{
//示例开始
GPIOAFEN &= ~BIT(0); //PA0作为GPIO使用
GPIOADE |= BIT(0); //PA0设置为数字IO
GPIOADIR |= BIT(0); //PA0方向设置为输入
GPIOAPU |= BIT(0); //作为输入,为了有稳定的输入状态(1或0),在外部没上拉或下拉时,需要根据实际情况开内部上拉或下拉
WDT_DIS(); //测试程序关看门狗,防止看门狗复位
while(1) {
delay_ms(100);
printf("GPIOA0 = 0x%X\n",GPIOA & BIT(0)); //打印读到的PA0状态
}
关于 GPIOASET/ GPIOACLR: 只对写入为1的位有效, 写入为0的位则不影响它以前的状态的. 如PA0输出高低
GPIOASET = BIT(0); //PA0输出高, 等效于 GPIOA |= BIT(0);
GPIOACLR = BIT(0); //PA0输出低, 等效于GPIOA &= ~BIT(0);
GPIO输出控制优化加速
此两个寄存器, 主要是优化IO输出高低电平而设置. 如设置PA0为高, 为了不影响GPIOA上的其它IO的操作, 以前程序一般这样写GPIOA |= BIT(0); 这里实际上有三个步骤:
1) 先读出GPIOA
2) 再把GPIOA | BIT(0)
3) 把或后的结果再写回GPIOA
而使用GPIOASET效优化后, 直接一步GPIOASET = BIT(0);
即达到 上面三步才能达到的效.
GPIOADE &= ~BIT(7);//模拟:0 or 数字:1
/* 功能选择 */
GPIOAFEN &= ~BIT(7);//普通IO:0 or 复用IO:1
/* 上下拉控制 */
GPIOAPU &= ~BIT(7);//10Kohm 上拉使能(输入有效) 0不用 1使能
GPIOAPU200K &= ~BIT(7);//200Kohm 上拉使能(输入有效) 0不用 1使能
GPIOAPU300 &= ~BIT(7);//300ohm 上拉使能(输入有效) 0不用 1使能
GPIOAPD &= ~BIT(7);//10Kohm 下拉使能(输入有效) 0不用 1使能
GPIOAPD300 &= ~BIT(7);//300ohm 下拉使能(输入有效) 0不用 1使能
GPIOAPD200K &= ~BIT(7);//200Kohm 下拉使能(输入有效) 0不用 1使能
/* 输入输出控制 */
GPIOADIR |= BIT(7);//方向选择 0输出 1输入
/* 驱动能力控制 */
GPIOADRV |= BIT(7);//驱动电流 写0 8 or 写1 32mA
/* 端口功能映射控制 */
FUNCMCON0 =
【31:28】UART1 RX mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to TX pin by UT1TXMAP select
1111: Clear these bits
【27:24】UART1 TX mapping
0000: no affect
0001: map to G1
0010: map to G2
1111: Clear these bits
【15:12】UART0 RX mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to G3
0100: map to G4
0101: map to G5
0110: map to G6
0111: map to TX pin by UT0TXMAP select
1111: Clear these bits
【11:8】UART0 TX mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to G3
0100: map to G4
0101: map to G5
0110: map to G6
0111: map to G7
1111: Clear these bits
【7:4】SPI0 mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to G3
1111: Clear these bits
【3:0】SD0 mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to G3
0100: map to G4
0101: map to G5
0110: map to G6
1111: Clear these bits
FUNCMCON1 =
【11:8】UART2 RX mapping
0000: no affect
0001: map to G1
0010: map to G2
0011: map to TX pin by UT2TXMAP select
1111: Clear these bits
【7:4】UART2 TX mapping
0000: no affect
0001: map to G1
0010: map to G2
1111: Clear these bits
FUNCMCON2 = /* 设置定时器 */
FUNCMCON2 = /* 设置MPDM interface mapping 和 PDM interface mapping */
对于G1这种IO组合名称定义,查阅config_define.h
,以及在Datasheet文件中找到相关定义
配置一个端口为串口
GPIOADE |= BIT(7);//数字
GPIOAPU |= BIT(7);//上拉10Kohm
GPIOADIR |= BIT(7);//方向选择1输入
GPIOAFEN |= BIT(7);//功能复用IO
GPIOADRV |= BIT(7);//驱动电流32mA
#define URX0MAP_TX (7<<12) //0111 0000 0000 0000
#define UTX0MAP_PA7 (1 << 8)//0000 0001 0000 0000
FUNCMCON0 = URX0MAP_TX | UTX0MAP_PA7;//RX0 Map To TX0, TX0 Map to G1
通过对照手册,设定UART1的相关寄存器设置,在config_define.h中
PS:数据手册中对UART1的RX脚没有G2相关描述,不过控制寄存器中存在G2组,这是datasheet无法与用户手册对应的地方!!!
/*****************************************************************************
* Module : uart1 Mapping选择列表 用户手册与数据手册冲突,以数据手册为准
*****************************************************************************/
#define UTX1MAP_PA7 (1 << 24) //G1 uart1 tx: PA7
#define UTX1MAP_VUSB (3 << 24) //G3 uart1 tx: VUSB
#define URX1MAP_PA6 (1 << 28) //G1 uart1 rx: PA6
/*****************************************************************************
* Module : uart2 Mapping选择列表 以数据手册为准
*****************************************************************************/
#define UTX2MAP_PB2 (2 << 4) //G2 uart2 tx: PB2
#define UTX2MAP_VUSB (3 << 4) //G3 uart2 tx: VUSB
#define UTX2MAP_CLR (0xF << 4) //清除映射
#define URX2MAP_PB1 (2 << 8) //G2 uart2 rx: PB1
#define URX2MAP_CLR (0xF << 8) //清除映射
配置为普通GPIO
/*****************************************************************************
* Module : GPIO list
*****************************************************************************/
#define IO_NONE 0
#define IO_PA0 1
#define IO_PA1 2
#define IO_PA2 3
#define IO_PA3 4
#define IO_PA4 5
#define IO_PA5 6
#define IO_PA6 7
#define IO_PA7 8
#define IO_PB0 9
#define IO_PB1 10
#define IO_PB2 11
#define IO_PB3 12
#define IO_PB4 13
#define IO_PE0 14
#define IO_PE1 15
#define IO_PE2 16
#define IO_PE3 17
#define IO_PE4 18
#define IO_PE5 19
#define IO_PE6 20
#define IO_PE7 21
#define IO_PF0 22
#define IO_PF1 23
#define IO_PF2 24
#define IO_PF3 25
#define IO_PB5 26
#define IO_MAX_NUM 26
#define IO_MUX_SDCLK 27
#define IO_MUX_SDCMD 28
#define IO_MUX_PWRKEY 29
#define IO_MUX_MICL 30
typedef struct {
psfr_t sfr; //GPIO SFR ADDR
u8 num;
u8 type; //type = 1,高压IO,没有300R的强上下拉电阻。 type = 0, 普通IO, 有内部300R上下拉电阻。
} gpio_t;
/* 配置IO,io_num就是上表中数字 */
gpio_cfg_init(gpio_t *g, u8 io_num);
简化普通GPIO配置
/* GPIO基础配置 */
#define GET_GPIO_DE_REG(port) GPIO##port##DE
#define GET_GPIO_FEN_REG(port) GPIO##port##FEN
#define GET_GPIO_DIR_REG(port) GPIO##port##DIR
#define GET_GPIO_DRV_REG(port) GPIO##port##DRV
#define GET_GPIO_DATA_REG(port) GPIO##port
#define GET_GPIO_SET_REG(port) GPIO##port##SET
#define GET_GPIO_CLR_REG(port) GPIO##port##CLR
/* 上拉控制 */
#define GET_GPIO_10K_PUP_REG_ADDR(port) GPIO##port##PU
#define GET_GPIO_200K_PUP_REG_ADDR(port) GPIO##port##PU200K
#define GET_GPIO_300_PUP_REG_ADDR(port) GPIO##port##PU300
#define GET_GPIO_10K_PUP_REG_ADDR(port) GPIO##port##PU
/* 下拉控制 */
#define GET_GPIO_10K_PD_REG_ADDR(port) GPIO##port##PD
#define GET_GPIO_200K_PD_REG_ADDR(port) GPIO##port##PD200K
#define GET_GPIO_300_PD_REG_ADDR(port) GPIO##port##PD300
#define GET_GPIO_10K_PD_REG_ADDR(port) GPIO##port##PD
/**
* @brief 配置32mA电流输出端口
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define GPIO_CONFIG_OUTPUT_MODE(port, pin) \
do{ \
GET_GPIO_DE_REG(port) |= BIT(pin); \
GET_GPIO_FEN_REG(port) &= ~BIT(pin); \
GET_GPIO_DIR_REG(port) &= ~BIT(pin); \
GET_GPIO_DRV_REG(port) |= BIT(pin); \
GET_GPIO_CLR_REG(port) = BIT(pin); \
}while(0)
/**
* @brief 配置8mA低电流驱动输出
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define GPIO_CONFIG_SAMALL_OUTPUT_MODE(port, pin) \
do{ \
GET_GPIO_DE_REG(port) |= BIT(pin); \
GET_GPIO_FEN_REG(port) &= ~BIT(pin); \
GET_GPIO_DIR_REG(port) &= ~BIT(pin); \
GET_GPIO_DRV_REG(port) &= ~BIT(pin); \
GET_GPIO_CLR_REG(port) = BIT(pin); \
}while(0)
/**
* @brief 配置引脚输入模式,默认10K上拉
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define GPIO_CONFIG_INPUT_MODE(port, pin) \
do{ \
GET_GPIO_DE_REG(port) |= BIT(pin); \
GET_GPIO_FEN_REG(port) &= ~BIT(pin); \
GET_GPIO_DIR_REG(port) |= BIT(pin); \
GET_GPIO_DRV_REG(port) |= BIT(pin); \
GET_GPIO_10K_PUP_REG_ADDR(port) |= BIT(pin); \
}while(0)
/**
* @brief 设置引脚高电平
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define SET_GPIO_TO_HIGH(port, pin) \
do{ \
GET_GPIO_SET_REG(port) = BIT(pin); \
}while(0)
/**
* @brief 设置引脚低电平
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define SET_GPIO_TO_LOW(port, pin) \
do{ \
GET_GPIO_CLR_REG(port) = BIT(pin); \
}while(0)
/**
* @brief 获取引脚电平
* @param port 端口
* @param pin 引脚号PAx
*
*/
#define GET_GPIO_DATA(port, pin) \
(GET_GPIO_DATA_REG(port) & BIT(pin))
static void Test_GPIO_Init(void)
{
// /* 使能数字GPIO PE5 */
// GPIOEDE |= BIT(5);
// /* 普通IO口功能 */
// GPIOEFEN &= ~BIT(5);
// /* 设置为输出 */
// GPIOEDIR &= ~BIT(5);
// /* 设置高电流驱动 */
// GPIOEDRV |= BIT(5);
// /* 设置为低电平 */
// GPIOE &= ~BIT(5);
GPIO_CONFIG_OUTPUT_MODE(E, 5);
}
static void Test_GPIO_Out(uint8_t GPIO_State)
{
if(GPIO_State)
{
// GPIOE |= BIT(5);
SET_GPIO_TO_HIGH(E, 5);
}
else
{
// GPIOE &= ~BIT(5);
SET_GPIO_TO_LOW(E, 5);
}
}
简化后的GPIO配置自定义LED
蓝讯有配置LED闪烁方式的工具(download),默认高电平点亮,内部配置IO看不到,所以在自己的电路中如果灯是低电平点亮,就无法使用工具进行配置LED,需要我们做下修改(配置IO、设置高低电平的接口由我们自己代码定义,闪烁周期方式由配置工具定义)
并不推荐这么做,功能由sdk实现的就尽量不动里面的代码,对后续升级sdk方便一点
1、在port_led.c
文件中增加如下接口
我们在bsp_led.c中实现这些接口:注意代码放置在.com区域
#define USER_BLUE_LED_PORT B
#define USER_BLUE_LED_PIN 4
#define USER_GREEN_LED_PORT B
#define USER_GREEN_LED_PIN 3
#define USER_RED_LED_PORT A
#define USER_RED_LED_PIN 6
#define USER_SET_LED_OFF_LEVEL 1 //1是高电平灯灭
AT(.com_text.led_disp)
void user_led_cfg_port_init(gpio_t *g)
{
if(&bled_gpio == g)
{
GPIO_CONFIG_OUTPUT_MODE(USER_BLUE_LED_PORT, USER_BLUE_LED_PIN);
}
if(&rled_gpio == g)
{
GPIO_CONFIG_OUTPUT_MODE(USER_RED_LED_PORT, USER_RED_LED_PIN);
}
}
AT(.com_text.led_disp)
void user_led_cfg_set_on(gpio_t *g)
{
if(&bled_gpio == g)
{
#if USER_SET_LED_OFF_LEVEL == 0
SET_GPIO_TO_HIGH(USER_BLUE_LED_PORT, USER_BLUE_LED_PIN);
#else
SET_GPIO_TO_LOW(USER_BLUE_LED_PORT, USER_BLUE_LED_PIN);
#endif
}
if(&rled_gpio == g)
{
#if USER_SET_LED_OFF_LEVEL == 0
SET_GPIO_TO_HIGH(USER_RED_LED_PORT, USER_RED_LED_PIN);
#else
SET_GPIO_TO_LOW(USER_RED_LED_PORT, USER_RED_LED_PIN);
#endif
}
}
AT(.com_text.led_disp)
void user_led_cfg_set_off(gpio_t *g)
{
if(&bled_gpio == g)
{
#if USER_SET_LED_OFF_LEVEL == 1
SET_GPIO_TO_HIGH(USER_BLUE_LED_PORT, USER_BLUE_LED_PIN);
#else
SET_GPIO_TO_LOW(USER_BLUE_LED_PORT, USER_BLUE_LED_PIN);
#endif
}
if(&rled_gpio == g)
{
#if USER_SET_LED_OFF_LEVEL == 1
SET_GPIO_TO_HIGH(USER_RED_LED_PORT, USER_RED_LED_PIN);
#else
SET_GPIO_TO_LOW(USER_RED_LED_PORT, USER_RED_LED_PIN);
#endif
}
}
xm配置文件脚本解释
# 添加一个子项控件,左侧标题,控件描述(底部提示)
config(SUB, "MIC参数", "蓝牙和ANC的MIC参数");
# 下拉控件,控件标题,底部提示,变量名,下拉列表数目,数目选项,默认0
config(LIST, "提示音语言选择", "选择系统的语言", LANG_ID, 4, "英文", "中文", "中英文(出厂默认英文)", "中英文(出厂默认中文)", 0);
# 开关控件,控件标题,控件注释(底部提示),变量名(实际小写),默认值0
config(CHECK, "MICR功能", "是否打开MICR功能", MICR_EN, 0);
# 列表控件,控件标题,控件注释(底部提示),变量名,BIT(固定语法), 占位大小3,默认值5,列表数目3
config(LISTVAL, "MICR通路选择", "选择MICR通路", MICR_SEL, BIT, 3, 5, 3, ("MICNR_PF2", 4), ("MICPR_PF3", 5), ("MICR_VUSB", 6),MICR_EN);
# 输入控件,控件标题,控件注释(底部提示),变量名,占位大小4,数值范围低0,数值范围高15,默认值3,依赖MICR_EN开关状态
config(BIT, "MICR内部偏置电阻大小", "MICR内部偏置电阻配置", MICR_BIAS_RESISTOR, 4, 0, 15, 3, MICR_EN);
# 输入控件,控件标题,控件注释(底部提示),变量名,数值范围低0,数值范围高13,默认值6,依赖MICR_EN开关状态 ps:此控件单独占一个字节
config(BYTE, "MICL模拟增益", "MICL模拟增益配置(3~42DB), Step 3DB", MICL_ANL_GAIN, 0, 13, 6, MICL_EN);
当增加一行配置项目,需要rebuild工程
config(BIT, "MICL内部偏置电阻xx大小", "MICL内部偏置电阻配置xx", MICL_BIAS_RESISTORx, 10, 1, 16, 4, MICL_EN);
downloader工具重新读取配置文件,才会增加新的配置项目,同时会自动更改以下文件。
- 配置更新
- xcfg配置数据结构更新
音频文件更新替换
当增加或替换音乐资源文件时,需要rebuild工程
音频文件目录位于:app\projects\earphone\Output\bin\res
app\projects\earphone\plugin\multi_lang.h
enum {
IDX_LANGUAGE_MP3,
IDX_LOW_BATTERY_MP3,
IDX_POWERON_MP3,
IDX_POWEROFF_MP3,
IDX_MAX_VOL_MP3,
IDX_SDCARD_MODE_MP3,
IDX_USB_MODE_MP3,
IDX_AUX_MODE_MP3,
IDX_CLOCK_MODE_MP3,
IDX_FM_MODE_MP3,
IDX_SPK_MODE_MP3,
IDX_PC_MODE_MP3,
IDX_BT_MODE_MP3,
IDX_CAMERA_MODE_MP3,
IDX_LEFT_CH_MP3,
IDX_RIGHT_CH_MP3,
IDX_PAIRING_MP3,
IDX_WAIT4CONN_MP3,
IDX_CONNECTED_MP3,
IDX_DISCONNECT_MP3,
IDX_CAMERA_ON_MP3,
IDX_CAMERA_OFF_MP3,
IDX_CALL_HANGUP_MP3,
IDX_CALL_REJECT_MP3,
IDX_REDIALING_MP3,
IDX_NUM_0_MP3,
IDX_NUM_1_MP3,
IDX_NUM_2_MP3,
IDX_NUM_3_MP3,
IDX_NUM_4_MP3,
IDX_NUM_5_MP3,
IDX_NUM_6_MP3,
IDX_NUM_7_MP3,
IDX_NUM_8_MP3,
IDX_NUM_9_MP3,
IDX_MUSIC_MODE_MP3,
IDX_GAME_MODE_MP3,
IDX_MAX_MP3,
};
typedef struct {
u32 *ptr;
u32 *len;
} res_addr_t;
const res_addr_t *res_get_ring_num(u8 index);
#if (LANG_SELECT == LANG_EN_ZH)
void multi_lang_init(uint lang_id);
extern const res_addr_t mul_lang_tbl[2][IDX_MAX_MP3];
#define MUTIL_LANG_TBL_EN 1
#define RES_BUF_LANGUAGE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LANGUAGE_MP3 ].ptr
#define RES_LEN_LANGUAGE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LANGUAGE_MP3 ].len
#define RES_BUF_LOW_BATTERY_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LOW_BATTERY_MP3].ptr
#define RES_LEN_LOW_BATTERY_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LOW_BATTERY_MP3].len
#define RES_BUF_POWERON_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_POWERON_MP3 ].ptr
#define RES_LEN_POWERON_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_POWERON_MP3 ].len
#define RES_BUF_POWEROFF_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_POWEROFF_MP3 ].ptr
#define RES_LEN_POWEROFF_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_POWEROFF_MP3 ].len
#define RES_BUF_SDCARD_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_SDCARD_MODE_MP3].ptr
#define RES_LEN_SDCARD_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_SDCARD_MODE_MP3].len
#define RES_BUF_USB_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_USB_MODE_MP3 ].ptr
#define RES_LEN_USB_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_USB_MODE_MP3 ].len
#define RES_BUF_AUX_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_AUX_MODE_MP3 ].ptr
#define RES_LEN_AUX_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_AUX_MODE_MP3 ].len
#define RES_BUF_CLOCK_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CLOCK_MODE_MP3 ].ptr
#define RES_LEN_CLOCK_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CLOCK_MODE_MP3 ].len
#define RES_BUF_FM_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_FM_MODE_MP3 ].ptr
#define RES_LEN_FM_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_FM_MODE_MP3 ].len
#define RES_BUF_SPK_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_SPK_MODE_MP3 ].ptr
#define RES_LEN_SPK_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_SPK_MODE_MP3 ].len
#define RES_BUF_PC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_PC_MODE_MP3 ].ptr
#define RES_LEN_PC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_PC_MODE_MP3 ].len
#define RES_BUF_MAX_VOL_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MAX_VOL_MP3 ].ptr
#define RES_LEN_MAX_VOL_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MAX_VOL_MP3 ].len
#define RES_BUF_BT_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_BT_MODE_MP3 ].ptr
#define RES_LEN_BT_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_BT_MODE_MP3 ].len
#define RES_BUF_CAMERA_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_MODE_MP3].ptr
#define RES_LEN_CAMERA_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_MODE_MP3].len
#define RES_BUF_LEFT_CH_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LEFT_CH_MP3 ].ptr
#define RES_LEN_LEFT_CH_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_LEFT_CH_MP3 ].len
#define RES_BUF_RIGHT_CH_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_RIGHT_CH_MP3 ].ptr
#define RES_LEN_RIGHT_CH_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_RIGHT_CH_MP3 ].len
#define RES_BUF_PAIRING_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_PAIRING_MP3 ].ptr
#define RES_LEN_PAIRING_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_PAIRING_MP3 ].len
#define RES_BUF_WAIT4CONN_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_WAIT4CONN_MP3 ].ptr
#define RES_LEN_WAIT4CONN_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_WAIT4CONN_MP3 ].len
#define RES_BUF_CONNECTED_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CONNECTED_MP3 ].ptr
#define RES_LEN_CONNECTED_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CONNECTED_MP3 ].len
#define RES_BUF_DISCONNECT_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_DISCONNECT_MP3 ].ptr
#define RES_LEN_DISCONNECT_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_DISCONNECT_MP3 ].len
#define RES_BUF_CAMERA_ON_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_ON_MP3 ].ptr
#define RES_LEN_CAMERA_ON_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_ON_MP3 ].len
#define RES_BUF_CAMERA_OFF_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_OFF_MP3 ].ptr
#define RES_LEN_CAMERA_OFF_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CAMERA_OFF_MP3 ].len
#define RES_BUF_CALL_HANGUP_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CALL_HANGUP_MP3].ptr
#define RES_LEN_CALL_HANGUP_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CALL_HANGUP_MP3].len
#define RES_BUF_CALL_REJECT_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CALL_REJECT_MP3].ptr
#define RES_LEN_CALL_REJECT_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_CALL_REJECT_MP3].len
#define RES_BUF_REDIALING_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_REDIALING_MP3 ].ptr
#define RES_LEN_REDIALING_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_REDIALING_MP3 ].len
#define RES_BUF_NUM_0_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_0_MP3 ].ptr
#define RES_LEN_NUM_0_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_0_MP3 ].len
#define RES_BUF_NUM_1_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_1_MP3 ].ptr
#define RES_LEN_NUM_1_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_1_MP3 ].len
#define RES_BUF_NUM_2_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_2_MP3 ].ptr
#define RES_LEN_NUM_2_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_2_MP3 ].len
#define RES_BUF_NUM_3_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_3_MP3 ].ptr
#define RES_LEN_NUM_3_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_3_MP3 ].len
#define RES_BUF_NUM_4_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_4_MP3 ].ptr
#define RES_LEN_NUM_4_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_4_MP3 ].len
#define RES_BUF_NUM_5_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_5_MP3 ].ptr
#define RES_LEN_NUM_5_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_5_MP3 ].len
#define RES_BUF_NUM_6_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_6_MP3 ].ptr
#define RES_LEN_NUM_6_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_6_MP3 ].len
#define RES_BUF_NUM_7_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_7_MP3 ].ptr
#define RES_LEN_NUM_7_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_7_MP3 ].len
#define RES_BUF_NUM_8_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_8_MP3 ].ptr
#define RES_LEN_NUM_8_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_8_MP3 ].len
#define RES_BUF_NUM_9_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_9_MP3 ].ptr
#define RES_LEN_NUM_9_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_9_MP3 ].len
#define RES_BUF_MUSIC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MUSIC_MODE_MP3 ].ptr
#define RES_LEN_MUSIC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MUSIC_MODE_MP3 ].len
#define RES_BUF_GAME_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_GAME_MODE_MP3 ].ptr
#define RES_LEN_GAME_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_GAME_MODE_MP3 ].len
定义MP3音频文件的代码,主要由IDX_MAX_MP3的枚举变量定义每个MP3文件的index。
res_addr_t.ptr 指定了每个MP3文件的存储地址
res_addr_t.len 指定了每个MP3文件的长度
如何增加新的音频文件
1、添加MP3文件
2、添加文件的宏定义
1)在res目录中增加MP3文件,本例中在zh目录中添加了num_10.mp3。
app\platform\bsp\bsp_music.h
enum {
TWS_RES_INVALID,
TWS_RES_NUM_0,
TWS_RES_NUM_1,
TWS_RES_NUM_2,
TWS_RES_NUM_3,
TWS_RES_NUM_4,
TWS_RES_NUM_5,
TWS_RES_NUM_6,
TWS_RES_NUM_7,
TWS_RES_NUM_8,
TWS_RES_NUM_9,
TWS_RES_NUM_10,//添加新文件的宏定义
...
app\platform\bsp\bsp_music.c
void tws_res_get_addr(u32 index, u32 *addr, u32 *len) {
switch(index) {
case TWS_RES_NUM_0:
*addr = RES_BUF_NUM_0_MP3;
*len = RES_LEN_NUM_0_MP3;
break;
...
case TWS_RES_NUM_9:
*addr = RES_BUF_NUM_9_MP3;
*len = RES_LEN_NUM_9_MP3;
break;
case TWS_RES_NUM_10://添加新文件的宏定义
*addr = RES_BUF_NUM_10_MP3;
*len = RES_LEN_NUM_10_MP3;
break;
app\projects\earphone\plugin\multi_lang.c
const res_addr_t mul_lang_tbl[2][IDX_MAX_MP3] = {
{
...
[IDX_NUM_0_MP3 ] = {&RES_BUF_ZH_NUM_0_MP3, &RES_LEN_ZH_NUM_0_MP3},
[IDX_NUM_1_MP3 ] = {&RES_BUF_ZH_NUM_1_MP3, &RES_LEN_ZH_NUM_1_MP3},
[IDX_NUM_2_MP3 ] = {&RES_BUF_ZH_NUM_2_MP3, &RES_LEN_ZH_NUM_2_MP3},
[IDX_NUM_3_MP3 ] = {&RES_BUF_ZH_NUM_3_MP3, &RES_LEN_ZH_NUM_3_MP3},
[IDX_NUM_4_MP3 ] = {&RES_BUF_ZH_NUM_4_MP3, &RES_LEN_ZH_NUM_4_MP3},
[IDX_NUM_5_MP3 ] = {&RES_BUF_ZH_NUM_5_MP3, &RES_LEN_ZH_NUM_5_MP3},
[IDX_NUM_6_MP3 ] = {&RES_BUF_ZH_NUM_6_MP3, &RES_LEN_ZH_NUM_6_MP3},
[IDX_NUM_7_MP3 ] = {&RES_BUF_ZH_NUM_7_MP3, &RES_LEN_ZH_NUM_7_MP3},
[IDX_NUM_8_MP3 ] = {&RES_BUF_ZH_NUM_8_MP3, &RES_LEN_ZH_NUM_8_MP3},
[IDX_NUM_9_MP3 ] = {&RES_BUF_ZH_NUM_9_MP3, &RES_LEN_ZH_NUM_9_MP3},
[IDX_NUM_10_MP3 ] = {&RES_BUF_ZH_NUM_10_MP3, &RES_LEN_ZH_NUM_10_MP3},//添加新文件的宏定义
[IDX_MUSIC_MODE_MP3 ] = {&RES_BUF_ZH_MUSIC_MODE_MP3, &RES_LEN_ZH_MUSIC_MODE_MP3},
[IDX_GAME_MODE_MP3 ] = {&RES_BUF_ZH_GAME_MODE_MP3, &RES_LEN_ZH_GAME_MODE_MP3},
},
};
app\projects\earphone\plugin\multi_lang.h
enum {
IDX_LANGUAGE_MP3,
IDX_LOW_BATTERY_MP3,
...
IDX_NUM_0_MP3,
IDX_NUM_1_MP3,
IDX_NUM_2_MP3,
IDX_NUM_3_MP3,
IDX_NUM_4_MP3,
IDX_NUM_5_MP3,
IDX_NUM_6_MP3,
IDX_NUM_7_MP3,
IDX_NUM_8_MP3,
IDX_NUM_9_MP3,
IDX_NUM_10_MP3,//添加新文件的宏定义
IDX_MUSIC_MODE_MP3,
IDX_GAME_MODE_MP3,
IDX_MAX_MP3,
};
typedef struct {
u32 *ptr;
u32 *len;
} res_addr_t;
const res_addr_t *res_get_ring_num(u8 index);
#if (LANG_SELECT == LANG_EN_ZH)
void multi_lang_init(uint lang_id);
extern const res_addr_t mul_lang_tbl[2][IDX_MAX_MP3];
#define MUTIL_LANG_TBL_EN 1
...
#define RES_BUF_NUM_0_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_0_MP3 ].ptr
#define RES_LEN_NUM_0_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_0_MP3 ].len
#define RES_BUF_NUM_1_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_1_MP3 ].ptr
#define RES_LEN_NUM_1_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_1_MP3 ].len
#define RES_BUF_NUM_2_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_2_MP3 ].ptr
#define RES_LEN_NUM_2_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_2_MP3 ].len
#define RES_BUF_NUM_3_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_3_MP3 ].ptr
#define RES_LEN_NUM_3_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_3_MP3 ].len
#define RES_BUF_NUM_4_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_4_MP3 ].ptr
#define RES_LEN_NUM_4_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_4_MP3 ].len
#define RES_BUF_NUM_5_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_5_MP3 ].ptr
#define RES_LEN_NUM_5_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_5_MP3 ].len
#define RES_BUF_NUM_6_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_6_MP3 ].ptr
#define RES_LEN_NUM_6_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_6_MP3 ].len
#define RES_BUF_NUM_7_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_7_MP3 ].ptr
#define RES_LEN_NUM_7_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_7_MP3 ].len
#define RES_BUF_NUM_8_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_8_MP3 ].ptr
#define RES_LEN_NUM_8_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_8_MP3 ].len
#define RES_BUF_NUM_9_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_9_MP3 ].ptr
#define RES_LEN_NUM_9_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_9_MP3 ].len
#define RES_BUF_NUM_10_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_10_MP3 ].ptr//添加新文件的宏定义
#define RES_LEN_NUM_10_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_NUM_10_MP3 ].len//添加新文件的宏定义
#define RES_BUF_MUSIC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MUSIC_MODE_MP3 ].ptr
#define RES_LEN_MUSIC_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_MUSIC_MODE_MP3 ].len
#define RES_BUF_GAME_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_GAME_MODE_MP3 ].ptr
#define RES_LEN_GAME_MODE_MP3 *mul_lang_tbl[sys_cb.lang_id][IDX_GAME_MODE_MP3 ].len
...
#define RES_BUF_NUM_0_MP3 RES_BUF_ZH_NUM_0_MP3
#define RES_LEN_NUM_0_MP3 RES_LEN_ZH_NUM_0_MP3
#define RES_BUF_NUM_1_MP3 RES_BUF_ZH_NUM_1_MP3
#define RES_LEN_NUM_1_MP3 RES_LEN_ZH_NUM_1_MP3
#define RES_BUF_NUM_2_MP3 RES_BUF_ZH_NUM_2_MP3
#define RES_LEN_NUM_2_MP3 RES_LEN_ZH_NUM_2_MP3
#define RES_BUF_NUM_3_MP3 RES_BUF_ZH_NUM_3_MP3
#define RES_LEN_NUM_3_MP3 RES_LEN_ZH_NUM_3_MP3
#define RES_BUF_NUM_4_MP3 RES_BUF_ZH_NUM_4_MP3
#define RES_LEN_NUM_4_MP3 RES_LEN_ZH_NUM_4_MP3
#define RES_BUF_NUM_5_MP3 RES_BUF_ZH_NUM_5_MP3
#define RES_LEN_NUM_5_MP3 RES_LEN_ZH_NUM_5_MP3
#define RES_BUF_NUM_6_MP3 RES_BUF_ZH_NUM_6_MP3
#define RES_LEN_NUM_6_MP3 RES_LEN_ZH_NUM_6_MP3
#define RES_BUF_NUM_7_MP3 RES_BUF_ZH_NUM_7_MP3
#define RES_LEN_NUM_7_MP3 RES_LEN_ZH_NUM_7_MP3
#define RES_BUF_NUM_8_MP3 RES_BUF_ZH_NUM_8_MP3
#define RES_LEN_NUM_8_MP3 RES_LEN_ZH_NUM_8_MP3
#define RES_BUF_NUM_9_MP3 RES_BUF_ZH_NUM_9_MP3
#define RES_LEN_NUM_9_MP3 RES_LEN_ZH_NUM_9_MP3
#define RES_BUF_NUM_10_MP3 RES_BUF_ZH_NUM_10_MP3//添加新文件的宏定义
#define RES_LEN_NUM_10_MP3 RES_LEN_ZH_NUM_10_MP3//添加新文件的宏定义
以上修改完成后,执行Rebuild,会自动生成app\projects\earphone\res.h。RES_BUF_ZH_NUM_10_MP3、RES_LEN_ZH_NUM_10_MP3的宏定义也会自动生成。如下所示:
#define RES_BUF_ZH_NUM_0_MP3 (*(u32 *)0x11000cb8)
#define RES_LEN_ZH_NUM_0_MP3 (*(u32 *)0x11000cbc)
#define RES_BUF_ZH_NUM_1_MP3 (*(u32 *)0x11000cd8)
#define RES_LEN_ZH_NUM_1_MP3 (*(u32 *)0x11000cdc)
#define RES_BUF_ZH_NUM_10_MP3 (*(u32 *)0x11000cf8)
#define RES_LEN_ZH_NUM_10_MP3 (*(u32 *)0x11000cfc)
最后可通过如下代码测试,是否能播放新添加的音频文件。
bsp_tws_res_music_play(TWS_RES_NUM_10);
软件开关机
大致流程:
- 关机:判断设备进入关机状态后,进入到此段代码,执行关闭外设,修改时钟,降低一切功耗,配置Powerkey脚为复位脚,死等
- 开机:当Powerkey按下,设备执行复位,正常运行代码检测开关机状态
power_on_check();
,当为关机状态时检测按下时长进行开机,否则再次进入到关机状态
void sfunc_pwrdown_do(u8 vusb_wakeup_en)
{
#if USER_TKEY
u32 tkey_wakeup_en = sys_cb.tkey_pwrdwn_en;
#endif
printf("pwrdwn: %d\n", vusb_wakeup_en);
RTCCON3 &= ~BIT(8); //rtc alarm wakeup disable
RTCCON8 &= ~BIT(15); //RI_EN_VUSBDIV = 0
RTCCON9 = 0x3ff; //Clr pending
#if USER_TKEY_LOWPWR_WAKEUP_DIS //电池无保护板且有内置触摸开关机功能方案,需要打开此宏
if (sys_cb.vbat < 3050) {
tkey_wakeup_en = 0; //低电关机, 关掉触模唤醒
}
if (tkey_wakeup_en) {
RTCALM = RTCCNT + 300; //定时5分钟唤醒检查电池电量
RTCCON3 |= BIT(8);
}
#endif
sfunc_power_save_enter();
if (!vusb_wakeup_en) {
RTCCON8 = (RTCCON8 & ~BIT(6)) | BIT(1); //disable charger function
#if !VUSB_SMART_VBAT_HOUSE_EN
vusb_wakeup_en = sfunc_pwrdown_w4_vusb_offline();
if (xcfg_cb.ch_box_type_sel == 3) { //5V完全掉电的仓
RTCCON3 &= ~BIT(12); //RTCCON3[12], INBOX Wakeup disable
}
#endif
}
RTCCON11 = (RTCCON11 & ~0x03) | BIT(2); //WK PIN filter select 8ms
uint rtccon3 = RTCCON3 & ~BIT(11);
uint rtccon13 = RTCCON13 & ~BIT(11);
#if CHARGE_EN
if ((xcfg_cb.charge_en) && (vusb_wakeup_en)) {
rtccon3 |= BIT(11); //VUSB wakeup enable
}
#endif
RTCCON3 = rtccon3 & ~(BIT(10) | BIT(14)); //关WK PIN,再打开,以清除Pending
PWRCON1 &= ~(0x1F<<14); //disable Flash Power Gate
PWRCON1 |= BIT(18); //pdown flash power gate
#if USER_TKEY
rtccon3 = RTCCON3;
rtccon3 |= BIT(3); //VDDCORE AON enable
rtccon3 &= ~BIT(5); //VDDCORE short disable
RTCCON3 = rtccon3;
delay_us(3);
if (tkey_wakeup_en) {
RTCCON3 |= BIT(14); //Touch key long press wakeup
}
#else
RTCCON0 &= ~BIT(0); //RC2M_RTC Disable
#endif
RTCCON0 &= ~BIT(4); //TKITF_EN disable
RTCCON0 &= ~BIT(13);
rtccon3 = RTCCON3 & ~0x17; //Disable VDDCORE VDDIO VDDBUCK, VDDXOEN
rtccon3 |= BIT(6); //Core power down enable, VDDCORE short disable
rtccon3 |= BIT(7); //RI_EN_VDDIO_AON RTC 2.9V LDO enable
#if USER_PWRKEY
if (sys_cb.wko_pwrkey_en) {
rtccon3 |= BIT(10); //WK pin wake up enable
#if POWER_KEY_USE_HIGHLEVEL
rtccon13 &=~BIT(4);
rtccon13 |= BIT(0) | BIT(8) | BIT(12)| BIT(16);
#else
rtccon13 |= BIT(0) | BIT(4) | BIT(12); //wk pin0 wakeup, input, pullup10k enable
#endif
}
#endif // USER_PWRKEY
#if !USER_TKEY
//保持电源,保证tkey通道能顺利关闭
RTCCON3 |= BIT(3); //VDDCORE AON enable
RTCCON3 &= ~BIT(5); //VDDCORE short disable
RTCCON0 |= BIT(17);
TKACON0 &= ~(BIT(23) | BIT(11)); //disable TK channel
TKACON1 &= ~(BIT(23) | BIT(11));
#endif
RTCCON |= BIT(5); //PowerDown Reset,如果有Pending,则马上Reset
RTCCON13 = rtccon13;
RTCCON3 = rtccon3;
LPMCON |= BIT(0);
asm("nop");asm("nop");asm("nop");
while (1);
}
串口功能
所有串口波特率支持1.5Mbps
高速串口配置
配置工具,配置引脚
接收数据:
需要注意的是,数据接收代码需放在(AT)如下区域,不可调用printf打印输出
/**
* @brief 高速串口数据接收
*
* @param data 数据
* @param len 数据长度
*/
AT(.com_huart.text)
void func_huart_aids_put_data(const uint8_t *data, uint16_t len)
{
CQ_putData(&CircularBuffer_Serial, data, len);
}
可以在外部循环中,加入如下代码,调试输出
uint32_t Len = CQ_getLength(CQ_Handle);
if(Len > 0)
{
CQ_ManualGetData(CQ_Handle, Temp_Buf, Len);
printf("REC:\n");
for(int i = 0; i < Len; i++)
{
printf("%02x ", Temp_Buf[i]);
}
}
发送数据:
需要注意的是,发送缓冲区需要定义在如下区域:
static uint8_t Send_Buf[20] AT(.aids_debug.cache);
huart_putcs(Send_Buf, 7);
UART0调试端口修改
修改位于config.h
文件,第39行,设置想要的引脚输出
#define UART0_PRINTF_SEL PRINTF_PA7//PRINTF_NONE //选择UART打印信息输出IO,或关闭打印信息输出
音频导出
音频导出利用的是HUART外设,此外设使用DMA发送,所以发送区域buf需要全局变量或者静态。
HUART波特率最高支持1.5Mbps,串口协议为一个停止位和起始位+8bit数据即每次10个bit
每秒最大发送:1500000 / 10 = 150000Bytes
每秒最大发送音频数据/16bit位宽:150000 / 2 = 75000点音频 相当于最高支持 75K的采样率无校验导出!
16K采样率的音频16bit位宽,传输为16000点/s == 32000Bytes/s,分成64点每包发出(64 * 2Bytes),需要250包,每包加上协议字段6个字节,就是增加250*6=1500Bytes,传输1s时长的16K数据需要32000 + 1500 = 33500Bytes,以1.5Mbps可以:150000 / 35000 ~= 4.4通道数据
1、增加导出代码
static const uint8_t auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
static const uint8_t auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
/**
* @brief modbusCRC calc with table
*
* @param data data
* @param data_len data len
* @return uint16_t
*/
uint16_t modbus_crc_return_with_table(uint8_t *data, uint16_t data_len)
{
uint8_t ucCRCHi = 0xFF;
uint8_t ucCRCLo = 0xFF;
int iIndex;
while(data_len--)
{
iIndex = ucCRCLo ^ *(data++);
//
//
ucCRCLo = (uint8_t)(ucCRCHi ^ auchCRCHi[iIndex]);
ucCRCHi = auchCRCLo[iIndex];
}
return (uint16_t)( ucCRCHi << 8 | ucCRCLo );
}
/**
******************************************************************
* @brief 音频导出接口加入音频
* @param [in]Source_Audio_Data 原始数据.
* @param [in]Result_Audio_Data 处理后数据.
* @return None.
* @author aron566
* @version V1.0
* @date 2021-10-29
******************************************************************
*/
#define ENBALE_EXPORT_CRC 1
#define AUDIO_CHANNEL_NUM 2
#define MONO_FRAME_SIZE 128
#define ENBALE_EXPORT_CRC 1
static int16_t Send_Buf[AUDIO_CHANNEL_NUM * MONO_FRAME_SIZE + 1 + 1 + 1] AT(.alg.cache);
AT(.com_text.alg)
void Audio_Export_Start(const int16_t *Source_Audio_Data, const int16_t *Result_Audio_Data)
{
Send_Buf[0] = 0x6605;//帧头
#if ENBALE_EXPORT_CRC
int16_t index = 2;
Send_Buf[1] = AUDIO_CHANNEL_NUM * MONO_FRAME_SIZE * sizeof(int16_t);
for(int16_t i = 0; i < MONO_FRAME_SIZE; i++)
{
Send_Buf[index] = Source_Audio_Data[i];
Send_Buf[index+1] = Result_Audio_Data[i];
index += 2;
}
/* 计算CRC */
uint16_t crc_val = modbus_crc_return_with_table((uint8_t *)Send_Buf, index*sizeof(int16_t));
memcpy(&Send_Buf[index], &crc_val, sizeof(uint16_t));
huart_putcs(Send_Buf, AUDIO_CHANNEL_NUM * MONO_FRAME_SIZE * sizeof(int16_t) + 6);
#else
int16_t index = 0;
for(int16_t i = 0; i < MONO_FRAME_SIZE; i++)
{
Send_Buf[index] = Source_Audio_Data[i];
Send_Buf[index+1] = Result_Audio_Data[i];
index += 2;
}
huart_putcs(Send_Buf, AUDIO_CHANNEL_NUM * MONO_FRAME_SIZE * sizeof(int16_t));
#endif
}
2、调用处理
Algorithm_Port_Start((const int16_t *)ptr, &Result_CH1_Ptr, &Result_CH2_Ptr);
#if USE_GPIO_TEST_ALGORITHM
SET_GPIO_TO_LOW(TEST_ALGORITHM_GPIO_PORT, TEST_ALGORITHM_GPIO_PIN);
#endif
#if ENABLE_EXPORT_FUNC
/* 音频导出 */
if(xcfg_cb.huart_en)
{
Audio_Export_Start((const int16_t *)ptr, (const int16_t *)Result_CH2_Ptr);
}
#endif
/* output DAC */
obuf1_put_mono_samples(Result_CH1_Ptr, Result_CH2_Ptr, samples);
3、打开音频生成工具,配置如下
- 工具已开源(github)
音频测试
注意事项:
1、杜绝USB连接工频220~交流电源,防止工频干扰,造成底噪问题!
- 示波器
- USB电源
- USB串口工具
2、测试算法时间,通常改变如下变量:
-
单帧处理点数
-
主频改变
源码修改调整
音频路径调整
文件bsp_audio.c
//AUX analog gain -42DB~+3DB
//MIC analog gain: 0~13(共14级), step 3DB (3db ~ +42db)
//adadc digital gain: 0~63, step 0.5 DB, 保存在gain的低6bit
const sdadc_cfg_t rec_cfg_tbl[] = {
/* 通道, 增益, 采样率, 通路控制, 样点数, 回调函数*/
{AUX_CHANNEL_CFG, (2 << 6), SPR_44100, ADC2DAC_EN, 256, aux_sdadc_callback}, /* AUX */
{MIC_CHANNEL_CFG, (8 << 6), SPR_44100, ADC2DAC_EN, 256, speaker_sdadc_callback}, /* SPEAKER */
{MIC_CHANNEL_CFG, (12 << 6), SPR_8000, ADC2DAC_EN, 128, bt_sdadc_callback}, /* BTMIC */
{MIC_CHANNEL_CFG, (12 << 6), SPR_48000, ADC2DAC_EN, 128, usbmic_sdadc_callback}, /* USBMIC */
{MIC_CHANNEL_CFG, (12 << 6), SPR_44100, ADC2SRC_EN, 256, karaok_sdadc_callback}, /* KARAOK */
{MIC_CHANNEL_CFG, (12 << 6), SPR_16000, ADC2DAC_EN, 256, opus_sdadc_callback}, /* opus */
{MIC_CHANNEL_CFG, (6 << 6), SPR_44100, ADC2SRC_EN, 256, ttp_sdadc_callback}, /* TRANSPARENCY */
};
void audio_path_init(u8 path_idx)
{
sdadc_cfg_t cfg;
memcpy(&cfg, &rec_cfg_tbl[path_idx], sizeof(sdadc_cfg_t));
#if FUNC_AUX_EN
if (path_idx == AUDIO_PATH_AUX) {
cfg.channel = (xcfg_cb.auxr_sel << 4) | xcfg_cb.auxl_sel;
cfg.gain = ((u16)xcfg_cb.aux_anl_gain << 6) | xcfg_cb.aux_dig_gain;
}
#endif // FUNC_AUX_EN
if (path_idx == AUDIO_PATH_BTMIC || path_idx == AUDIO_PATH_KARAOK || path_idx == AUDIO_PATH_BTVOICE || path_idx == AUDIO_PATH_OPUS || path_idx == AUDIO_PATH_TTP) {
if (path_idx == AUDIO_PATH_BTMIC) {
if (sys_cb.hfp_karaok_en) {
memcpy(&cfg, &rec_cfg_tbl[AUDIO_PATH_KARAOK], sizeof(sdadc_cfg_t));
cfg.sample_rate = SPR_48000;
} else {
#if BT_AEC_EN || BT_ALC_EN || BT_NLMS_AEC_EN
if (xcfg_cb.bt_aec_en) {
cfg.callback = bt_aec_process;
} else if (xcfg_cb.bt_alc_en) {
cfg.callback = bt_alc_process;
} else {
cfg.callback = bt_sco_tx_process;
}
#endif
}
}
cfg.channel = bt_mic_channel_get();
cfg.gain = ((u16)BT_ANL_GAIN << 6) | BT_DIG_GAIN;
}
if (bt_mic_sel == 2) {
cfg.channel = CH_VUSBL;
cfg.gain = ((u16)BT_ANL_GAIN << 6) | BT_DIG_GAIN;
}else if (bt_mic_sel == 6) {
cfg.channel = CH_VUSBR;
cfg.gain = ((u16)BT_ANL_GAIN << 6) | BT_DIG_GAIN;
}
#if SDADC_DRC_EN
sdadc_drc_init((u8 *)RES_BUF_EQ_SDADC_DRC, RES_LEN_EQ_SDADC_DRC);
#endif
sdadc_init(&cfg);
}
在不改变原有功能上增加自己的音频路径
增加路径表后:
/* 音频路径号 */
#define AUDIO_PATH_AIDS_MIC_TEST 9
/* 接收处理 */
void audio_export_process(u8 *ptr, u32 samples, int ch_mode);
#if FUNC_AUDIO_EXPORT_EN
#define audio_export_callback audio_export_process
#endif
const sdadc_cfg_t rec_cfg_tbl[] = {
/* 通道, 增益, 采样率, 通路控制, 样点数, 回调函数*/
{AUX_CHANNEL_CFG, (2 << 6), SPR_44100, ADC2DAC_EN, 256, aux_sdadc_callback}, /* AUX */
{MIC_CHANNEL_CFG, (8 << 6), SPR_44100, ADC2DAC_EN, 256, speaker_sdadc_callback}, /* SPEAKER */
{MIC_CHANNEL_CFG, (12 << 6), SPR_8000, ADC2DAC_EN, 128, bt_sdadc_callback}, /* BTMIC */
{MIC_CHANNEL_CFG, (12 << 6), SPR_48000, ADC2DAC_EN, 128, usbmic_sdadc_callback}, /* USBMIC */
{MIC_CHANNEL_CFG, (12 << 6), SPR_44100, ADC2SRC_EN, 256, karaok_sdadc_callback}, /* KARAOK */
{MIC_CHANNEL_CFG, (12 << 6), SPR_16000, ADC2DAC_EN, 256, opus_sdadc_callback}, /* opus */
{MIC_CHANNEL_CFG, (6 << 6), SPR_44100, ADC2SRC_EN, 256, ttp_sdadc_callback}, /* TRANSPARENCY */
{MIC_CHANNEL_CFG, (8 << 6), SPR_44100, ADC2DAC_EN, 256, audio_export_callback} /* AUDIO EXPORT*/
};
初始化路径
/* 等待淡出完成 */
dac_fade_wait();
/* 初始化音频路径配置 */
audio_path_init(7);
/* 启动音频路径 */
audio_path_start(7);
/* 淡入 */
dac_fade_in();
音频处理
/* 定义音频接收回调处理 */
void audio_export_process(u8 *ptr, u32 samples, int ch_mode)
{
/* 放大音频 */
uint16_t *rptr16 = (uint16_t *)ptr;
uint32_t *rptr32 = (uint32_t *)ptr;
if(ch_mode == 0)
{
for(uint32_t i = 0; i < samples; i++)
{
rptr16[i] <<= 6;
}
}
else
{
for(uint32_t i = 0; i < samples; i++)
{
rptr32[i] <<= 4;
}
}
/* DAC输出 */
sdadc_pcm_2_dac(ptr, samples, ch_mode);
}
MIC设置
配置工具设置如下
PE7供电代表MIC_bias使用的是PE7脚输出供电!!!
DAC输出通道设置
DAC的输出通道由变量xcfg_cb.dac_sel
控制,所以此配置由donwloader工具调整:
/**
* @brief 输出音频到DAC
*
* @param buf_CH1
* @param buf_CH2
* @param samples
*/
AT(.com_text.dac)
static void obuf1_put_mono_samples(void *buf_CH1, void *buf_CH2, u32 samples)
{
u16 *ptr_CH1 = buf_CH1;
u16 *ptr_CH2 = buf_CH2;
u32 sample = 0;
while(samples--)
{
#if USE_HW_EQ //music接口调节EQ
if(AUBUFCON & BIT(8))
{
break;
}
sample = *ptr_CH2++;
sample <<= 16;
sample |= *ptr_CH1++;
AUBUFDATA = sample;
#else
if(AUBUF1CON & BIT(8))
{
break;
}
sample = *ptr_CH2++;
sample <<= 16;
sample |= *ptr_CH1++;
AUBUF1DATA = sample;
#endif
}
}
代码量统计
添加宏
/**
* @brief 获取指定段地址
* @param section_nam 段名
* @param save_val 保存变量名
*/
#define GET_SECTION_ADDR(section_nam, save_val) extern const uint32_t section_nam; \
save_val = (uint32_t)(§ion_nam)
设置段ld
文件中,变量存储段
__AIDS_Start = .;
*(.aids.*)
__AIDS_End = .;
设置代码中的变量地址
/* 算法选择 */
static ALGORITHM_FUNCTION_Typdef_t Algorithm_Func_Select AT(.aids.cache);
/* 两帧音频数据 */
static int16_t Algorithm_2FRAME_Data[MONO_FRAME_SIZE * 2] AT(.aids.cache);
/* 算法输出存储区 */
static int16_t Algorithm_Result_Data[MONO_FRAME_SIZE] AT(.aids.cache);
添加打印,输出变量占用大小
uint32_t save_val_s, save_val_e;
GET_SECTION_ADDR(__AIDS_Start, save_val_s);
GET_SECTION_ADDR(__AIDS_End, save_val_e);
printf("s 0x%08X, e 0x%08X size %u=>%fKB\n", save_val_s, save_val_e, save_val_e - save_val_s, (float)(save_val_e - save_val_s)/1024.f);
设置代码占用地址
__AIDS_CODE_Start = .;
*(.text.aidis*)
__AIDS_CODE_End = .;
/* 代码指定 */
AT(.text.aidis.wdrc)
static void WDRC_Cal(const int16_t *Src, int16_t *pOut)
{
/* 代码 */
}
/* 常量指定 */
static const uint8_t auchCRCHi[] AT(.text.aidis.ro) = {0x00};
打印代码及常量占用Flash大小,单位字节数
uint32_t save_val_s, save_val_e;
GET_SECTION_ADDR(__AIDS_CODE_Start, save_val_s);
GET_SECTION_ADDR(__AIDS_CODE_End, save_val_e);
printf("s 0x%08X, e 0x%08X size %u=>%fKB\n", save_val_s, save_val_e, save_val_e - save_val_s, (float)(save_val_e - save_val_s)/1024.f);
充电检测
bsp_charge.c
///兼容某些维持电压的仓: 耳机入仓, 电池仓的电压不会自动升到5V, 需要手动按仓的按键。
AT(.com_text.bsp.charge)
void bsp_charge_inbox_process(void)
{
#if USE_RELEASE_VERSION == 0
static u8 Last_Charge_State = 5;
static u8 Last_InBox = 5;
static u8 Last_InBoxCheck = 5;
static u8 Last_SysCharge_State = 5;
if(Last_Charge_State != CHARGE_DC_IN() ||
Last_InBox != CHARGE_INBOX() ||
Last_InBoxCheck != bsp_charge_inbox_check() ||
Last_SysCharge_State != sys_cb.charge_sta)
{
Last_Charge_State = CHARGE_DC_IN();//
Last_InBox = CHARGE_INBOX();
Last_InBoxCheck = bsp_charge_inbox_check();
Last_SysCharge_State = sys_cb.charge_sta;
/* 检测VUSB状态 */
printf("VUSB = %u.\n", Last_Charge_State);
printf("IN VOX = %u.\n", Last_InBox);
printf("Last_InBoxCheck = %d %d.\n", Last_InBoxCheck, chbox_cb.inbox_rtt_check);
printf("Last_SysCharge_State = %d.\n", Last_SysCharge_State);
switch(Last_Charge_State)
{
case 2:
printf("2 Charge Full\n");
break;
case 1:
printf("1 Charge Begin\n");
break;
case 0:
printf("0 Charge Off\n");
break;
}
}
#endif
/* 充电关机 */
if (sys_cb.charge_sta && (xcfg_cb.chg_inbox_pwrdwn_en))
{
msg_enqueue(EVT_CHARGE_INBOX);
}
static u32 inbox_tick = 0;
u8 sta;
if ((chbox_cb.inbox_rtt_check) && (!CHARGE_DC_IN())) {
if (!tick_check_expire(inbox_tick, 10)) {
return;
}
inbox_tick = tick_get();
sta = bsp_charge_inbox_check();
#if VUSB_SMART_VBAT_HOUSE_EN
bsp_vhouse_inbox_sta(sta);
#endif
if ((2 == sta) && (xcfg_cb.chg_inbox_pwrdwn_en)) {
msg_enqueue(EVT_CHARGE_INBOX);
}
}
}
入仓关机功能
如果软件开关机功能被关闭,入仓关机功能受到影响,可能无法关机,需要做如下修改
BLE开发指南
BLE使能
1、代码部分
2、配置参数,BLE使能
3、接收数据入口
加入以下代码测试,手机连接BLE,使用服务UID为0xFF13
printf("REC: \n");
for(u8 i = 0; i < len; i++) {
printf("%02x ", ptr[i]);
}
可以看到收到了如下打印信息:
4、发送数据入口,手机端监听UUID为0xFF14
的服务
加入以下代码:
ble_tx_notify(gatts_app_notify_base.att_index, ptr, len);
我们使用UUID为0xFF13
对BLE从机发送数据,可以看到收到如下的通知:
调试打印信息:
FOAT功能
1、代码部分
SPP协议
BLE协议
2、配置参数
3、转换bin文件为fota的升级文件
4、手机蓝牙发送固件即可完成升级
对于BLE下的FOAT,手机需要通过UUID为0xFF15
提供的读写特征服务下发数据。
BT蓝牙名称及配置修改
目标:修改蓝牙名称增加随机后缀。
修改配置工具,增加名称修改使能开关,参考MAC地址随机功能
config(SUB, "蓝牙配置", "蓝牙的相关配置");
config(CHECK, "配置蓝牙名", "是否配置蓝牙名", CONFIG_BT_NAME_EN, 0);
config(TEXT, "蓝牙名称", "手机上可以看到的蓝牙名称", BT_NAME, 32, "Bluetrum-TWS", CONFIG_BT_NAME_EN);
config(MAC, "蓝牙后缀", "蓝牙名后缀", BT_SUFFIX, 6, 00:00:00:00:00:00, FF:FF:FF:FF:FF:FF, 00:00:00:00:00:01, CONFIG_BT_NAME_EN);
config(MAC, "蓝牙地址", "蓝牙的MAC地址", BT_ADDR, 6, 41:42:00:00:00:00, 41:42:FF:FF:FF:FF, 41:42:00:00:00:01);
config(LISTVAL, "长按几秒开机进配对", "是否支持长按N秒开机直接进入配对状态", BT_PWRKEY_NSEC_DISCOVER, BIT, 3, 0, 8, ("不支持", 0), ("2秒", 1), ("3秒", 2), ("4秒", 3), ("5秒", 4), ("6秒", 5), ("7秒", 6), ("8秒", 7));
config(CHECK, "配对提示音", "是否播放配对提示音", WARNING_BT_PAIR, 1);
config(CHECK, "连接两部手机功能", "是否支持连接两部手机功能(打开<TWS功能>后无效)", BT_2ACL_EN, 0);
config(CHECK, "音乐播放功能", "是否支持蓝牙音乐播放功能", BT_A2DP_EN, 1);
config(CHECK, "音乐音量同步", "是否支持A2DP音量与手机同步", BT_A2DP_VOL_CTRL_EN, 0, BT_A2DP_EN);
config(CHECK, "通话功能", "是否支持蓝牙通话的功能", BT_SCO_EN, 1);
config(CHECK, "私密接听功能", "是否使用手动私密接听(切换到手机端接听)", BT_HFP_PRIVATE_EN, 1, BT_SCO_EN);
config(CHECK, "来电报号功能", "是否支持来电报号功能", BT_HFP_RING_NUMBER_EN, 1, BT_SCO_EN);
config(CHECK, "串口功能", "是否支持蓝牙串口的功能", BT_SPP_EN, 0);
config(CHECK, "拍照功能", "是否支持蓝牙HID拍照的功能", BT_HID_EN, 0);
config(CHECK, "按键HID连接/断开功能", "是否支持蓝牙HID服务,按键手动连接/断开", BT_HID_MANU_EN, 0, BT_HID_EN);
config(CHECK, "HID默认不连接", "HID服务默认不连接,需要按键手动连接", BT_HID_DISCON_DEFAULT_EN, 0, BT_HID_EN);
config(LEVEL, 0x00);
config(CHECK, "HID独立自拍器模式", "是否支持独立自拍器模式功能", FUNC_BTHID_EN, 0);
config(TEXT, "HID蓝牙名称", "手机上可以看到的独立自拍器模式蓝牙名称", BTHID_NAME, 32, "BT-Photo", FUNC_BTHID_EN);
config(LEVEL, 0x03);
config(CHECK, "BLE控制功能", "是否支持BLE音乐控制的功能", BLE_EN, 0);
config(TEXT, "BLE名称", "手机上可以看到的BLE蓝牙名称", LE_NAME, 32, "LE-Remoter", CONFIG_BT_NAME_EN);
config(CHECK, "快速测试使能", "快速测试使能", QTEST_EN, 0);
config(LIST, "耳机拿起后状态", "设置耳机拿起后的状态,以测试盒设置为准", QTEST_PICKUP_STATUS, 4, "不操作", "拿起关机", "拿起复位", "断开蓝牙连接后关机" 0,QTEST_EN);
config(U32, "快测配对ID选择", "测试盒可根据ID选择是否可进行快速配对", QTEST_TWS_PAIR_ID, 0, 0x7fffffff, 0x38383930, QTEST_EN);
重新编译后出现如下开关标签:
SDK修改:
修改经典蓝牙名
const char *bt_get_local_name(void)
{
#if 1
#if IODM_TEST_MODE
bt_get_new_name(xcfg_cb.bt_name);
#endif // IODM_TEST_MODE
#if FUNC_BTHID_EN
if (is_bthid_mode()) {
return xcfg_cb.bthid_name;
}
#endif // FUNC_BTHID_EN
/* 更改蓝牙名称 */
char name[32];
if(xcfg_cb.config_bt_name_en)
{
sprintf(name, "%s-%02X%02X", xcfg_cb.bt_name, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
else
{
sprintf(name, "%s-%02X%02X", bt_local_name, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
snprintf(xcfg_cb.bt_name, 32, "%s", name);
return xcfg_cb.bt_name;
#else
return bt_local_name;
#endif // 1
}
修改BLE蓝牙名:
u32 ble_get_scan_data(u8 *scan_buf, u32 buf_size)
{
memset(scan_buf, 0, buf_size);
u32 data_len = sizeof(scan_data_const);
memcpy(scan_buf, scan_data_const, data_len);
//读取BLE配置的蓝牙名称
/* 更改蓝牙名称 */
char name[32];
if(xcfg_cb.config_bt_name_en)
{
sprintf(name, "%s-%02X%02X", xcfg_cb.le_name, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
else
{
sprintf(name, "%s-%02X%02X", BT_NAME_DEFAULT, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
snprintf(xcfg_cb.le_name, 32, "%s", name);
int len;
len = strlen(xcfg_cb.le_name);
if (len > 0) {
memcpy(&scan_buf[2], xcfg_cb.le_name, len);
data_len = 2 + len;
scan_buf[0] = len + 1;
}
return data_len;
}
u32 ble_get_adv_data(u8 *adv_buf, u32 buf_size)
{
memset(adv_buf, 0, buf_size);
u32 data_len = sizeof(adv_data_const);
memcpy(adv_buf, adv_data_const, data_len);
//读取BLE配置的蓝牙名称
/* 更改蓝牙名称 */
char name[32];
if(xcfg_cb.config_bt_name_en)
{
sprintf(name, "%s-%02X%02X", xcfg_cb.le_name, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
else
{
sprintf(name, "%s-%02X%02X", BT_NAME_DEFAULT, xcfg_cb.bt_suffix[4], xcfg_cb.bt_suffix[5]);
}
snprintf(xcfg_cb.le_name, 32, "%s", name);
int len;
len = strlen(xcfg_cb.le_name);
if (len > 0) {
memcpy(&adv_buf[9], xcfg_cb.le_name, len);
adv_buf[7] = len + 1;
data_len = 9 + len;
}
return data_len;
}
硬件算法支持
FFT变换
FFT缓冲区不可为局部变量!
/* 参数说明 */
typedef enum {
RDFT_128 = 0,
RDFT_256,
RDFT_512,
} RDFT_LEN;
typedef struct {
void *in_addr; //输入地址
void *pwr_addr; //功率谱输出地址,为NULL则关闭功率谱输出
void *out_addr; //输出地址
u8 fft_shft; //功率谱打开后,FFT输出放大倍数
RDFT_LEN size; //size:0(128), 1(256), 2(512)
u8 window_en :1; //只有fft 512有效(不对客户开放使用)
u8 input_type :1; //input type:0,half word; 1,word
u8 isr_en :1; //是否打开中断(不对客户开放使用)
} fft_cfg_t;
typedef struct {
void *in_addr;
void *out_addr;
RDFT_LEN size; //size:0(128), 1(256), 2(512)
u8 window_en :1; //只有ifft 512有效
u8 output_type :1; //output type:0,half word; 1,word
u8 overlap_en :1; //硬件overlap
u8 isr_en :1; //是否打开中断(不对客户开放使用)
} ifft_cfg_t;
/* 测试代码 */
/* 256点FFT/iFFT测试,输入为资源音频文件RES_BUF_INFILE_PCM,FFT输入为word,无overlap:*/
#define NFFT 256
static u32 fft_in[NFFT] AT(.fft_test);
static u32 fft_out[NFFT] AT(.fft_test);
static u32 ifft_out[NFFT] AT(.fft_test);
void sys_fft_test(void)
{
WDT_DIS();
fft_cfg_t fft_t;
ifft_cfg_t ifft_t;
memset(&fft_t, 0, sizeof(fft_cfg_t));
fft_t.size = RDFT_256;
fft_t.input_type = 1;
fft_t.in_addr = fft_in;
fft_t.out_addr = fft_out;
memset(&ifft_t, 0, sizeof(ifft_cfg_t));
ifft_t.size = RDFT_256;
ifft_t.output_type = 1;
ifft_t.in_addr = fft_out;
ifft_t.out_addr = ifft_out;
s16 *infile = (s16 *)RES_BUF_INFILE_PCM;
for (int i = 0; i < 20; i++) {
for (int j = 0; j < NFFT; j++) {
fft_in[j] = infile[j];
}
infile += NFFT;
printf("### INPUT:\n");
print_r32(fft_in, NFFT);
fft_hw(&fft_t);
printf("### FFT:\n");
print_r32(fft_out, NFFT);
ifft_hw(&ifft_t);
}
}
/* 256点FFT/iFFT测试,输入为资源音频文件RES_BUF_INFILE_PCM,FFT输入为word,有overlap: */
#define NFFT 256
static u32 fft_in[NFFT] AT(.fft_test);
static u32 fft_out[NFFT] AT(.fft_test);
static u32 ifft_out[NFFT] AT(.fft_test);
void sys_fft_test(void)
{
WDT_DIS();
fft_cfg_t fft_t;
ifft_cfg_t ifft_t;
memset(&fft_t, 0, sizeof(fft_cfg_t));
fft_t.size = RDFT_256;
fft_t.input_type = 1;
fft_t.in_addr = fft_in;
fft_t.out_addr = fft_out;
memset(&ifft_t, 0, sizeof(ifft_cfg_t));
ifft_t.size = RDFT_256;
ifft_t.output_type = 1;
ifft_t.in_addr = fft_out;
ifft_t.out_addr = ifft_out;
ifft_t.overlap_en = 1;
s16 *infile = (s16 *)RES_BUF_INFILE_PCM;
for (int i = 0; i < 20; i++) {
for (int j = 0; j < NFFT/2; j++) {
fft_in[NFFT/2 + j] = infile[j];
}
infile += NFFT / 2;
printf("### INPUT:\n");
print_r32(fft_in, NFFT);
fft_hw(&fft_t);
printf("### FFT:\n");
print_r32(fft_out, NFFT);
ifft_hw(&ifft_t);
memcpy(fft_in, &fft_in[NFFT/2], sizeof(fft_in)/2);
}
}
基础数学计算
void log2_hw(u32 X, u8 Q, math_out_t *output);
void log10_hw(u32 X, u8 Q, math_out_t *output);
void ln_hw(u32 X, u8 Q, math_out_t *output);
void pow2_hw(u32 X, u8 Q, math_out_t *output);
void powe_hw(u32 X, u8 Q, math_out_t *output);
void sqrt_hw(u32 X, u8 Q, math_out_t *output);
int sqrt64_hw(int hi, int lo);
void log10_hw(u32 X, u8 Q, math_out_t *output) == (实际公式为:Z = log10(Y))
参数说明
输入参数X,Q:Y = X / (2^Q);
输出参数math_out_t *output:
typedef struct {
u32 outdat; //A = outdat / 2^31
u32 outexp; //B = 2^outexp
} math_out_t; //result = A * B
所以输出:Z = (outdat / 2^31) * (2 ^ outexp);
计算log10(0.123456), Q = 16, X = 0.123456 * 2 ^ 16 = 8090:
/* 示例代码 */
math_out_t output;
//计算log10(0.123456), Q = 16, X = 0.123456 * 2 ^ 16 = 8090
log10_hw(8090, 16, &output);
printf("log10(0.123456):%d %d\n", output.outdat, output.outexp);
输出:log10(0.123456):-487764073 2
即log10(0.123456) = -487764073 / (2 ^ 31) * (2 ^ 2) = -0.908531
- Q值是左移位数,对小数放大
- X值是放大后的数值
/* 测试代码 */
void Test_Math_HW(void)
{
/* 计算log2(8) */
log2_hw(8, 0, &out);
DE_PRINTF("log2_hw(8, 0, &out);\r\n");
DE_PRINTF(" A = 0x%08X = out.outdat / 2^31, B = %u = 2^out.outexp\r\n", out.outdat, out.outexp);
float A = (float)out.outdat / (1U << 31);
uint32_t res = A * (1U << out.outexp);
DE_PRINTF("RES = A*B = %08X\r\n", res);
}
数组带入运算
/* 求2^(X(定点数)-1)次方和 exp固定为1*/
void MATH_FUNC_PORT(power2_q15)(
const q15_t * pSrc,
uint32_t blockSize,
q63_t * pResult)
{
uint32_t blkCnt; /* Loop counter */
q63_t sum = 0; /* Temporary result storage */
q15_t in; /* Temporary variable to store input value */
#if USE_BT8922X_CORE
BASE_MATH_Typedef_t output1;
BASE_MATH_Typedef_t output2;
uint32_t exp;
/* Initialize blkCnt with number of samples */
blkCnt = blockSize;
while (blkCnt > 0U)
{
/* C = 2^A[0] + 2^A[1] ... + 2^A[blockSize-1] */
/* Compute Power and store result in a temporary variable, sum. */
in = *pSrc++;
pow2_hw(in, 15, (math_out_t *)output1);
/* exp固定位1 */
exp = output1.exp - 1;
output1.data = << exp;
in = *pSrc++;
pow2_hw(in, 15, (math_out_t *)output2);
/* exp固定位1 */
exp = output2.exp - 1;
output2.data = << exp;
sum += (output1.data + output2.data)
/* Decrement loop counter */
blkCnt -= 2;
}
*pResult = sum;
}
对于编译器报没有对应的软件浮点实现问题,需要连接对应的库:
程序下载更新
参考:其他博主图片
注意事项
芯片框架简述
1、框架总述
LX蓝牙芯片采用最近比较流行的RISC-V(32位)开源内核架构 + 国产RT-Thread操作系统.不过从代码上来看, 操作系统代码已经被封装到库中, 一般用户可以不用涉及操作系统代码, 降低了开发难度.
LX芯片“冯·诺依曼结构”, 即代码与数据的统一编址. 框架结构大致如下:
芯片内部一般会封装一颗512K或1M SpiFlash, 用于存放代码及资源文件/参数记忆等. SpiFlash和芯片之间通过spi接口进行通信.
首先,代码不会直接在SpiFlash上运行, SpiFlash中所有程序及数据均需要先通过spi接口加载到芯片RAM中, CPU再从RAM中取指令或数据运行.
2、com区和bank区
基于上述的程序存储框架, LX芯片在程序编写时, 有两个重要的概念: com区(公共区) 和 bank区.
com区(公共区):
芯片上电, 一般从Mask程序区开始运行, 在进入main函数之前, 程序会先把com区程序从Flash加载到芯片内部Ram. 由于在程序的整个生命周期内, com区程序会一直保留在RAM中. CPU执行com区代码会很快.
但由于芯片RAM有限, com区一般分配在几十K以内.
bank区(也称为flash区):
Flash中的bank区(存储区)一般是几百K以上.
RAM中的bank程序运行区(类似cache), 一般从几K到几十K不等.
由于RAM中的bank区远远小于FLASH中的bank区, 所以CPU会根据需要不断把Flash(bank程序存储区)中的代码动态替换(加载)到 RAM中的bank程序运行区运行. 由于芯片与Flash之间通过spi进行通信, bank区代码执行速度相对比较慢.
3、开发时需要注意:
上所述, 开发时需要注意如下:
-
1)中断函数(及其子函数)必须放入com区, 否则(放入bank区)会导致死机.
中断响应需要非常及时, com区程序常驻于RAM中, CPU可以迅速响应中断函数.
如果中断函数放入bank区, 中断响应时,可能该bank区还未加载到RAM中, 还需要先加载再运行,耗时相对较慢. 为了防止中断响应慢,芯片做了限定: 中断中加载bank区代码则直接死机.
另外需要特别注意: 中断函数中不能有switch语句, swith语句编译后生成的跳转常量表会默认放到bank区, 引起中断函数访问bank区死机. 请用if-else语句代替switch语句.
一些实时性要求比较高的代码,首选放入com区.
-
2).函数前没有AT指定存放段的代码, 默认放入bank区, bank区代码自动加载(或替换), 不需要人工干预.
由于bank区加载到RAM, 需要spi通信, IO翻转会有一定干扰, 在一些对干扰敏感的应用中, 如FM, 可以把所有相关程序放入同一个命名的bank, 这样bank加载时, 会大概率地把所有同一bank中的程序一次性加载到RAM中,减少程序运行时可能不断多次地加载引起的spi通信干扰.
4、函数放入com区的写法
在函数前面加入AT(.指定段名到com区) 即可.示例如 usr_tmr1ms_isr函数前面的AT(.com_text.timer)
AT(.com_text.timer)
void usr_tmr1ms_isr(void)
{
gui_scan();
.....
}
在map.text中可以看到, usr_tmr1ms_isr位于 0x20e4a.位于0x20000~ (0x20000+34K) 的com区内.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3t0fn9C9-1659060313100)(BT8892B_Aids.image/image-20211105212820603.png)]
另外有一点, printf参数中的字符串常量也是默认放在bank区中, 如果在中断中调用printf, 也需要把字符串常量放入com区.
如下:
AT(.com_text.str1)
const char str1[] = "Com String";
AT(.com_text.str2) / / 注意每个字符串前都需要增加 AT定位到com区去.
const char str2[] = "val = %d\n";
//调用printf时, 直接使用该字符串即可:
AT(.com_text.timer)
void usr_tmr5ms_isr(void)
{ printf(str1);
printf(str2, test_val);
……
}