Low Latency Mic SDK Developer Handbook
Quick Start
Overview
Purpose
This document describes the common configuration, usage and debugging methods of the SDK, so that users can get started quickly and solve most of the problems encountered in the actual development process.
Terminology
TBD
Hardware Related
Hardware Block Diagram
EVB Development Board
For details of EVB development board, please refer to the document "Telink TLSR9518A Generic Starter Kit Hardware Guide_internal_V1.0.pdf".
(1) Line-in Input
There are left and right linein inputs, which can be single-ended or differential input. EVB defaults to differential connection.
Single left linein:
Short connection:
AIPL1_LINEIN(J34_32)--TL_AIPL1(J34_31)
AINL1_LINEIN(J34_34)--TL_AINL1(J34_33)
Disconnect:
AIPL1_MIC(J34_28)--TL_AIPL1(J34_27)
AINL1_MIC(J34_30)--TL_AINL1(J34_29)
MICBIAS_AIL1(J34_36)--TL_MICBIAS(J34_35)
Single right linein:
Short connection:
AIPR1_LINEIN(J20_32)--TL_AIPR1(J20_31)
AIPR1_LINEIN(J20_34)--TL_AINL1(J20_33)
Disconnect:
AIPR1_MIC(J20_28)--TL_AIPR1(J20_27)
AINR1_MIC(J20_30)--TL_AINR1(J20_29)
MICBIAS_AIR1(J20_36)--TL_MICBIAS(J20_35)
(2) AMIC Input
There are left and right amic inputs, which can be single-ended or differential input. EVB defaults to differential connection.
Single left amic:
Short connection:
AIPL1_MIC(J34_28)--TL_AIPL1(J34_27)
AINL1_MIC(J34_30)--TL_AINL1(J34_29)
MICBIAS_AIL1(J34_36)--TL_MICBIAS(J34_35)
Disconnect:
AIPL1_LINEIN(J34_32)--TL_AIPL1(J34_31)
AINL1_LINEIN(J34_34)--TL_AINL1(J34_33)
Single right amic:
Short connection:
AIPR1_MIC(J20_28)--TL_AIPR1(J20_27)
AINR1_MIC(J20_30)--TL_AINR1(J20_29)
MICBIAS_AIR1(J20_36)--TL_MICBIAS(J20_35)
Disconnect:
AIPR1_LINEIN(J20_32)--TL_AIPR1(J20_31)
AIPR1_LINEIN(J20_34)--TL_AINL1(J20_33)
(3) DMIC Input
DMIC only needs 2 signal lines data and clk, and the clk frequency fixed at 3 MHz.
Dual DMIC has 1 signal line data and 2 clk, dual dmic share data, 2 clk timing is the same, in the clk rising edge to collect one dmic data, in the clk falling edge to collect the other dmic data.
Short connection:
Left and right mic shared data: DMICDAT锛圝20-10锛�-- TL_PD4_DMICDAT(J20-9)
Left mic: DMICCK1(J20-8)--TL_PD6_DMICCLK1(J20-7)
Right mic: DMICCK2(J20-6)--TL_PD6_DMICCLK2(J20-5)
(4) USB Interface
Short connection:
DM(J34-2)--TL_PA5_DM (J34-1)
DP(J34-4)--TL_PA6_DP (J34-3)
(5) I2S Interface
I2S Master-Slave mode is supported, but it is recommended that the TRLS9XXX I2S be the Master, as the Master does not need to do asynchronous resampling, which reduces computing power consumption.
Example Run
Hardware Preparation
Three EVB boards and three USB cables are required.
Description:
Pairing button: short press to enter the pairing state, double-click to exit the pairing state.
Connection status indicator: fast flashing for pairing status, slow flashing for reconnection status, constant light for connection status.
As shown in the figure above, the L Linein input and Lineout output interface are used. If the analog mic is used as the input, the Tx port J34 needs to be short connected as shown in the red box in the figure below.
Software Burning
Two EVB as TX to burn img_proj_ultra_ll_mic_tx.bin, one EVB as RX to burn img_proj_ultra_ll_mic_rx.bin (the firmware is in the release_bin directory), the burning procedure is as follows:
Step 1: Prepare the Telink burner and connect it as shown in the figure below.
Step 2: Open the Telink BDT.exe program.
Step 3: Click "Activate".
Step 4: Click "File", and click "Open" in the drop-down menu.
Select the firmware to be burned, and then click "Download", as shown below:
Step 5: Burn the MAC address, open "Memory Access", operate and fill in the MAC address in the order of the numbers in the figure below, if you want to burn the MAC address as: 0xa4c138000000, then fill in: 00 00 00 38 c1 a4, as shown below, after filling in, press "Enter" to burn it. (For more operations of the BDT tool, you can visit: http://wiki.telink-semi.cn/wiki/IDE-and-Tools/Burning-and-Debugging-Tools-for-all-Series/)
Note that when the burner fails to activate, you need to upgrade the firmware of the burner, as shown below, click "Help", select "Upgrade", load the firmware under the burning software path, finally unplug and re-plug the burner.
Run
After powering on again, short press TX0 and SW2 button on RX to pair TX0; then press TX1 and SW2 button on RX to pair TX2. After the pairing is successful, while the blue lights of TX0 and TX1 are always on, they will light up the green and white lights of the channel respectively, while the Rx will light up the green and white lights at the same time, as shown in the figure:
Finally, the audio can be monitored through the Lineout interface.
Note:
- Lineout on the Tx end outputs the audio encoded by the local AMIC pickup audio and then decoded, while Lineout on the Rx end outputs the audio decoded from the encoded audio data sent from the remote Tx0 and Tx1 respectively.
- The Tx0 with a green light is the left channel and the Tx1 with a white light is the right channel.
Compile SDK
Import
When you first open the Andesight IDE, a dialog box for selecting workspace will pop up, you need to click "Browse..." to select a directory as your workspace, any directory can be selected as the workspace directory.
Select "File -> Import", as shown below.
In the pop-up window, click "General" and select "Existing Projects into Workspace".
Enter the path to the SDK in the "Select root directory" box, or click "Browser" directly and select the directory where the SDK is located, then tick the SDK and click "Finish" to import the project. (Note: It is recommended to distinguish between the IDE working path and the path where the SDK source code is located, so as to facilitate the opening of multiple projects.)
Compile
The bin to be burned after compilation is the merged firmware (with img* header), including:
(1) The boot project with bootloader: for program jump and ota. (proj_boot*)
(2) App project: the actual running of the software. (proj_ultra_ll_mic*)
The IDE script will merge the above two projects into the firmware with _img_ header and save it to the app project generation directory, such as:
b91m_ultra_ll_mic_sdk\proj_ultra_ll_mic_tx\output\img_proj_ultra_ll_mic_tx.bin
Therefore, when compiling the app project for the first time, it is necessary to compile the _proj_boot_device_ and _proj_boot_dongle_ projects first for subsequent generation of merged firmware.
Note:
When compiling for the first time, you must compile two boot projects first, and then compile the main project, otherwise an error will be reported.
Then compile the 3 (receiver 16bit), 4 (transmitter 24bit) or 5 (receiver 16bit), 6 (transmitter 24bit) projects.
Common Tools
Telink BDT
The full name of Telink BDT tool is "Telink Burning and Debugging Tool". For specific functions, please refer to the "Tool User Guide" document. Several common application scenarios are listed below.
(1) Multi-bin File Download Tool
Telink BDT is based on TWS and implements the function of burning multiple bin files, and is also applicable to this SDK. It is used as follows:
Step 1: Click "Tool", and select "TWS Tool" in the drop-down menu.
Step 2: Select the "Download Tool" page, check Bin File1, Bin File2 and BT MAC respectively, and select the corresponding project firmware, tone file and fill in the MAC address, click "Download" to burn the program, tone and MAC address at the same time.
(2) Tone Independent Burning
The SDK supports the tone function by default requires the tone file to be burned in the specified address. The detailed steps are as follows:
Step 1: Open Telink BDT, click "Setting", and enter 0x80000 at "Download Addr" in the pop-up window.
Step 2: Select 48k ADPCM tone file.
Step 3: Click "Download" to burn the tone.
(3) MAC Address Independent Read and Write
Interference may occur when the MAC addresses of the two Tx ends are the same, so the MAC address can be read and written through Telink BDT.
Step 1: Open Telink BDT, click "Activate", and in the case of Activate OK, click "Tool", select "Memory Access", and in the pop-up window select "Flash" for the memory type, enter "6" for the size and "FF000" for the addr.
Then press the "tab" key to read the mac address stored at this address: e0 33 8e 8b 70 2b.
Step 2: Enter the mac address to be modified in the data column: e0 33 8e 8b 70 2c, and press "Enter" to write.
Press the "tab" key again to read the address data and find that the writing is successful:
(4) Burn the Specified usb_id to TX End
In 2-to-1 mode, the default start address of the TX end is 0x120 (determined by the 0xFFFF0 address where FLASH_SYS_USER_USBID_ADDR is located).
void user_init(void)
{
......
#if (APP_BUILD == APP_MIC_TX)
uint16_t usb_id = 0x120;
uint8_t flag = 0;
app_flash_read(FLASH_SYS_USER_USBID_ADDR, sizeof(flag), (u8 *)&flag); //FLASH_SYS_USER_USBID_ADD - 0x7FFF0
if (flag != 0xFF) {
usb_id = 0x100 + flag;
}
......
}
If you need to view multiple TX logs, you need to modify the usb_id addresses of the different TX to distinguish them, so as to be consistent with the pid configuration of the risc_v_tdb tool.
Then synchronise the corresponding values in the pid configuration of the risc_v_tdb tool.
risc_v_tdb
(1) USB Print and View
The USB print log can use the following function, the parameter str is the string to be printed, ph is the data to be printed, and n is the length of byte. If you only need to print the string or data, you can write 0 for the unneeded parameter.
void usb_send_str_data (char *str, u8 *ph, int n);
This function has commonly used encapsulation as follows:
void usb_send_str_data (char *str, u8 *ph, int n)
int usb_send_str_int (char *str,int w);
void usb_send_str_u32s (char *str, u32 d0, u32 d1, u32 d2, u32 d3);
#define my_dump_str_data(en,s,p,n)
#define my_dump_str_u32s(en,s,d0,d1,d2,d3)
Developers can call the functions.
To support USB printing, you first need to configure APP_MODE to APP_MODE_DEBUG_ONLY or APP_MODE_DEBUG_HID in the app_config.h file. (In APP_MODE_AUDIO_AUTO mode you cannot use risc_v_tdb tool to view log, need to use USB to serial port tool.)
......
///// 4 different mode //////////
#if (APP_BUILD == APP_MIC_RX)
#define APP_MODE APP_MODE_DEBUG_ONLY //APP_MODE_AUDIO_AUTO
#else
#define APP_MODE APP_MODE_DEBUG_ONLY // APP_MODE_DEBUG_ONLY or APP_MODE_DEBUG_HID
#endif
//////////////////////////// APP Mode: setting //////////////////////////////
......
Then open different configurations according to different pids. The default pid in the Tx end is 0x1209218, and the default pid in the Rx end is 0x1239218, which consists of usb_id and pid.
(a) Tx end usb_id
void user_init(void)
{
......
#if (APP_BUILD == APP_MIC_TX)
uint16_t usb_id = 0x120;
uint8_t flag = 0;
app_flash_read(FLASH_SYS_USER_USBID_ADDR, sizeof(flag), (u8 *)&flag); //FLASH_SYS_USER_USBID_ADD - 0x7FFF0
if (flag != 0xFF) {
usb_id = 0x100 + flag;
}
......
}
Since Tx is divided into Tx0 and Tx1 in 2-to-1 mode, a usb_id definition is possible.
(b) Rx end usb_id
void user_init(void)
{
......
#elif(APP_BUILD ==APP_MIC_RX)
uint16_t usb_id = 0x123;
#endif
......
}
Define the full pid in the app_usb_desc.c file.
#if (APP_MODE & FLAG_PRINTER_DEBUG)
const USB_Descriptor_Device_t my_device_desc_audio = MYDESC_DEVICE(0x0110, 0x248a, 0x9218);
Then create different configurations according to different pids, namely Tx0, Tx1 and Rx.
(2) Flash Read and Write
(a) risc_v_tdb supports the function of online flash writing, so as to realize the requirement of direct online upgrade program during the debugging process:
Step 1: Select the corresponding configuration, after the connection is successful, click "In File" and select the firmware to be upgraded. (Note: Before writing the firmware, the MAC address can be modified synchronously by modifying the MAC option box.)
Step 2: Click "File_to_Flash" to write the firmware.
(b) risc_v_tdb also supports the function of reading flash and saving it as a file:
Step 1: Select the configuration that supports reading flash and saving it as a file.
Step 2: Click "Out File", select the bin file to be written to flash.
Step 3: Enter the start address of the flash to be read and the read length.
Step 4: Click "Flash_to_File" to save the read flash to the bin file.
(3) Send Command
The risc_v_tdb supports the function of sending commands to control the program: based on the USB Print mode, users can add the control function of the project in the my_usb_audio_debug interface. The currently supported functions are: pairing test control, EMI test control, tone test control, data transmission test control, and noise reduction function test control.
The detailed control methods are as follows:
Step 1: Use risc_v_tdb to connect to the board, and open the the corresponding macros for function configuration, such as: EMI_TEST_ENABLE, TTONE_EN, TRANSMIT_DATA_EN, APP_NS_ENABLE, etc.
Step 2: Enter the corresponding command in the command box, such as 11 00 20, and press "Enter".
Among them, 11 is the command header, 00 is the corresponding value for the pairing test control and 20 is the pairing test incoming parameter. Other test controls are the same as above.
(4) VCD Waveform
VCD Basics
Value Change Dump (VCD) is an ASCII-based format for dump files generated by EDA logic simulation tools. In 1996, IEEE Std. 1364-1995 defined the standard four-value VCD format along with the Verilog hardware description language. Six years later, the extended VCD format defined in IEEE Std. 1364-2001 supports the recording of signal strength and directionality. The simple and compact structure of the VCD format makes its use ubiquitous and has been extended to non-Verilog tools, such as the VHDL simulator GHDL and various kernel trackers. One limitation of the format is that it cannot record values in memory.
The content of the VCD file is divided into title section, variable definition section, variable initialization section, and value change section. The title section contains the timestamp, simulator version number, and timescale, it maps the time increments listed in the value change section to simulated time units. The variable definition section contains range information and a list of signals instantiated within a given range. The initialization section contains the initial values of all dumped variables. The value change section contains a series of chronological value changes for the signals in a given simulation model.
Getting the VCD file is mainly to write the value change.
VCD Function
(a) Print event, this function will reverse the value of the variable twice, and form a pulse. The function ID must be an event type variable, and the function will only output information about the value change, not the time information, and its value change will follow the latest timestamp.
log_event(en,id)
e.g. log_event(1, SLET_timestamp) //Will reverse the value of SLEV_reserved twice, 0->1,1->0.
(b) Print event and time, this function will reverse the value of the variable twice, and form a pulse. This function ID must be an event with tick type variable, and the function will output the value change and time information.
log_tick(en,id)
e.g. log_tick(1, SLET_timestamp)
(c) Print data, the following three functions are to print 1/8/16bit data. "en" is enable, "id" is the variable name (the signal quantity in the previous subsection needs to use the variable name of 1/8/16bit data type), and "b" is the changed value. It will write the value change of the variable and the current time to the VCD file.
log_task(en,id,b) //print 1bit data
e.g. log_task(1, SL01_TX_EN,1) //Change the value of SLET_timestamp to 1 at the current time point
log_task(1, SL01_TX_EN,0) //Change the value of SLET_timestamp to 0 at the current time point
log_b8(en,id,d) //print 8bit data
e.g. log_ b8 (1, SL08_lmp_rx_code,1)
log_b16(en,id,d) //print 16bit data
e.g. log_ b16 (1, SL16_bt_CLKN_HS,1)
risc_v_tdb Generates VCD Files to View Timing
When APP_MODE is configured as APP_MODE_DEBUG_ONLY or APP_MODE_DEBUG_HID, risc_v_tdb supports Telink's own protocol to view vcd waveforms based on usb print, and it can view the execution of each function. The specific viewing steps are as follows:
Step 1: When connected, click "Def" and select the log_def_stack.h file in the common\usb_dbg path.
Step 2: Click "In File", select the firmware generated in the firmware\_proj_ultra_ll_mic_tx_\output path (take _proj_ultra_ll_mic_tx_ as an example).
Step 3: Click "VCD" to collect:
After collecting a certain amount, click "Stop", and the vcd file will be automatically saved to the output folder corresponding to the project.
Then click "View" to jump to the waveform viewing tool.
Step 4: In the pop-up tool, click "File" and select "Read Save File".
Select the mic-i1.gtkw file (in the document, the editor directly stores it in the riscv-tool root directory, this file is used to add the waveform to be viewed to the Waves window, and the user can also manually drag the desired signal to view).
The waveform can be seen as shown in the figure below.
The entire gtkwave tool page is as follows:
Click "EVENT" in the window 1;
The window 2 displays the captured signals;
Input character can be convenient to find the signal in the window 3;
The window 4 is the displayed signal (double-click the signal to be displayed in the window 2);
The window 5 is the timing display of the entire signal.
Serial VCD Assistant Generates VCD File
When APP_MODE is configured as APP_MODE_AUDIO_AUTO, the vcd waveform file cannot be generated using the risc_v_tdb tool, and a serial port VCD assistant is required to generate the VCD waveform file.
Firstly, you need to enable the UART_VCD_EN macro in the app_config.h file.
The SDK will call uart_vcd_init interface in user_init to initialise the corresponding serial port parameters for generating vcd waveforms. The baud rate is 3000000, and the default TX pin is PD2.
void user_init(void)
{
......
#if UART_VCD_EN
uart_vcd_init(UART0, 3000000, UART0_TX_PD2, UART0_RX_PD3, DMA7);
#endif
......
}
Then, the tool folder UART_VCD_TOOL needs to be placed in the same level directory as the waveform viewing tool gtkw folder (here the UART_VCD_TOOL folder is placed directly into the riscv folder).
Use the USB to serial port tool to connect the board and PC, open the serial port VCD assistant risc_v_tdb.exe, click Scan, select the corresponding serial port, click open to start generating VCD waveform files.
Click "VCD" to start/stop capturing waveform.
Wave is to view VCD waveform (if log prints . .gtkwave.exe Not Found, the error is caused by the fact that UART_VCD_TOOL and gtkw are not in the same level directory).
"Header" is the header file of VCD waveform capturing.
"Output" is the generated VCD waveform file.
View Log via Serial Port
When APP_MODE is configured as APP_MODE_AUDIO_AUTO, the risc_v_tdb tool cannot be used to view the log. At this time, you need to use the usb to serial tool to view it.
The SDK calls app_set_usb_desc_task interface to initialise uart1. The baud rate is 1000000, and the default pins are TX-PE0 and RX-PE2.
void app_set_usb_desc_task (int reset)
{
if (reset)
{
.......
uart1_init(1000000);
uart_set_tx_dma_config(UART1, DMA5);
return;
......
}
......
}
Use the USB to serial tool to connect the board and PC, open the usb to serial tool TL_UART_Log_Tool, click "scan", select the corresponding serial port, click "open" to view the log. The default in the SDK is PE0 output log.
OTA Tool
(1) OTA Introduction
The OTA function is implemented in the BootLoader. After the chip is powered on, the BootLoader program is first executed. The main process of the BootLoader program is as follows:
(a) Read the value of the analog register 0x3c (set to: areg_v_0x3c, power on after power off, the value of areg_v_0x3c is 0x00).
(b) Determine if areg_v_0x3c is equal to 0x4b. If not, verify APP code, if verification is successful, then execute APP code.
(c) Initialize functional modules such as USB and RF, and run BootLoader function.
(2) Two Methods to Support OTA
Method 1: There is a USB port in the RX side and no USB port in the TX side. You can use the combination Dongle+RX, Device+TX, send commands via button or USB HID or other interface to let TX and RX enter the BootLoader at the same time, and RX connect to PC via USB. The host computer can communicate and upgrade RX through USB, and can upgrade TX through RX.
Method 2: Both TX and RX have no USB interface. You can use the combination Device+RX, Device+TX, and make an upgrade board (EVB board available) to burn Dongle firmware. When the Dongle is powered on, TX or RX can receive commands through the button/serial port, jump to the BootLoader (Device), the Dongle will automatically connect to the Device, and perform OTA upgrades for the TX and RX respectively through the host computer.
(3) Dongle and Device Switching Method
The SDK defaults RX as Dongle and TX as Device. It can be changed by modifying the SDK project settings. As shown in the figure below, the RX end as the Dongle, If you want to use the RX as the Device, you only need to change the red box in the figure below to "_proj_boot_device_\output\_proj_boot_device_". Tx is the same.
(4) OTA Operation Steps
(a) Compile _proj_boot_device_ and _proj_boot_dongle_ projects.
(b) Open the vendor/_proj_low_latency_mic/app_ui_EVB/app_ui.h file, find the TRANSMIT_DATA_EN macro (create one if not), set it to 1, and compile and burn TX and RX respectively.
(c) Open two RISC-V-TDB tools and select the corresponding ini configuration files respectively, if there is no corresponding ini file, select an ini file, click the "INI" button, and modify the prnid and vcdid of TX and RX. TX's prnid=01209218, vcdid=11209218; RX's prnid=01239218, vcdid=11239218. After setting, re-select the ini file, "RISC-V-TDB -- Found" appears in the title bar, and the following log appears, indicating that the setting is correct.
(d) Press SW2 of TX0 and RX at the same time to enable pairing. After successful pairing, the green or white lights of TX0, TX1 and RX will light up at the same time, indicating that the connection is successful.
(e) As shown above, at the bottom of the RISC-V-TDB tool corresponding to RX, if the green lights of TX and RX are on, enter the command 11 05 00 to upgrade TX0; if the white light is on, enter the command 11 05 01 to upgrade TX1, press "Enter" to send the command, after 1 second TX and RX enter the BootLoader separately.
(f) Open the usb_ota.exe tool, the tool title bar displays DFU Found, indicating that the Dongle has been successfully identified. As shown in the figure below, "Remote Device Connected" appears, indicating that the Dongle and Device are successfully connected.
(g) Upgrade Dongle via USB interface:
a) Click the "USB File" button, and in the pop-up file selection box, select the firmware to be upgraded corresponding to Dongle. b) Click the corresponding "DL" button on the right, and the following log will appear, indicating that the Dongle upgrade is successful.
(h) Upgrade the Device (Tx0) through the USB interface:
a) Click the "Remote" button, and in the pop-up file selection box, select the firmware to be upgraded corresponding to the Device. b) Click the corresponding "DL0" button to the right of the "Remote" button, and the following log will appear, indicating that the Device upgrade is successful. After the Device upgrade is successful, both Dongle and Device will restart.
(i) To upgrade another TX (TX1):
a) Burn the firmware to TX1 and reset it. b) Re-execute step 4.
EMI Tool
EMI test tool introduction: based on USB HID, you need to configure APP_MODE to APP_MODE_DEBUG_HID (the editor uses this mode here, so as to combine with log to explain) or APP_MODE_AUDIO_AUTO, while enabling the EMI_TEST_ENABLE macro to enable the EMI test function.
Step 1: Open the emi_test_hid_tool tool and display USB Connected, which means the connection is successful.
The default pid is 9218. If the pid of the device to be tested has changed, select the corresponding pid in the pid drop-down box and click "Set Pid".
Step 2: Configure the EMI parameters, click "Enter EMI", as shown in the figure, the EMI test will be printed in the RISC-V TDB tool, and print the corresponding configuration parameters.
Step 3: Click "Exit EMI" to exit EMI mode (Note: to modify the configuration, you need to click Exit EMI to exit EMI mode first, and then click Enter EMI to re-enter EMI, thus enabling the configuration modification).
Version Number Tool
The SDK will add version number information to the generated BIN based on the version number script during the compilation process. Developers can use the version number tool to view the version number of the corresponding BIN.
(1) tl_version.h
MAKE_VER(major, minor, revision) //used to configure 24-bit version number, such as 0.0.0
TL_VS_SDK_NAME(str) //Used to refer to the SDK, must be cited, otherwise an error is reported
TL_VS_VER(name, value) //Used to configure the version number for the module, optional, generally used for algorithm module, codec module version number definition
TL_VS_STRING(name, str) //Used to configure string, optional
TL_VS_INT(name, value) //Used to configure decimal variable, optional
TL_VS_HEX(name, value) //Used to configure hexadecimal variable, optional
TL_VS_BYTE_ARRAY(name, ...) //Used to configure array variables, separated by commas, optional
(2) Application
Add the following code to the main.c file in any project:
TL_VS_SDK_NAME("telink_b91m_ll_mic_sdk");
TL_VS_VER(sdk, MAKE_VER(1, 1, 0));
TL_VS_VER(lc3_plus, MAKE_VER(1, 4, 2));
TL_VS_VER(lc3_a, MAKE_VER(1, 4, 2));
TL_VS_VER(plc, MAKE_VER(1, 2, 1));
TL_VS_VER(asrc, MAKE_VER(1, 0, 0));
TL_VS_STRING(Serial_Number, "cky24-q8qrh-x3kmr-c6bcy-t847y ll_mic 2022");
TL_VS_INT(Rx_Num, 3);
TL_VS_HEX(Tx_Type, 15);
TL_VS_BYTE_ARRAY(rf, 1,2,3,4,5,6,7,8,9);
After compilation, version-related information can be displayed in the compilation option box:
(3) Example
Open the generated bin file such as img_proj_ultra_ll_mic_rx.bin, the tail address of the hex view is as follows, the version related information are stored to the tail of the bin file:
Open the version number tool VersionViewerTool.exe, click "OPEN" to select the bin file or drag the bin file directly into the tool box (the first of the two Info appearing is the version number information of the boot project, the second is the version number information of the corresponding project):
Tone Tools
The SDK supports the tone function with ADPCM decoding, so it is necessary to convert the custom tone to ADPCM format file with the tone tool.
Users only need to place the customised tone file in WAV format into the tone_tool directory and execute the pcm2adpcm.exe program to generate the tone file tone_adpcm.bin in the same directory.
SDK Introduction
Overview
The microphone SDK is generally divided into a transmitter side and a receiver side according to its function, referred to below as TX and RX. Usually TX is the signal sampling end, the signal is captured and then compressed/encoded and the data is sent via a wireless 2.4GHz signal. Taking the ultra-low latency SDK as an example, the corresponding projects are _proj_ultra_ll_mic_tx and _proj_ultra_ll_mic_240_tx. RX is to receive the data sent from TX end through the 2.4GHz signal, decode/decompress the data and output audio through USB Audio or Line-out. Taking the ultra-low latency SDK as an example, the corresponding projects are _proj_ultra_ll_mic_rx and _proj_ultra_ll_mic_240_rx.
Software Structure
After importing the SDK project in the IDE, the displayed file structure is shown in the following figure. There are 10 top-level folders: algorithm, application, boot, common, drivers, incs, proj_lib, stack, vendor.
algorithm: Algorithm related programs, aes_ccm, crypto, ecc.
application: Provides some generic application handlers such as print, keyboard etc.
boot: Provides the software bootloader for the chip, i.e. the assembly process after the MCU is powered on or waked up by deepsleep, to set up the environment for the C program to run later.
codec: Codec related algorithms, such as filter, resample, etc.
common: Provides some general cross-platform handling functions, such as memory handling functions, string handling functions, etc.
drivers: Provides hardware settings and peripheral drivers that are closely related to the MCU, such as clock, flash, I2C, USB, GPIO, UART, etc.
incs: Public header file directory.
proj_lib: Stores the library files necessary for the SDK to run (e.g. libB91_driver_i2.a). The BLE stack, RF driver, PM driver and other files are encapsulated in the library files, and users cannot see the source files.
stack: Stores the header files related to the BLE/BT protocol stack. The source files are compiled into library files and are not visible to the user.
vendor: Stores user application layer code.
boot: Stores the cstartup_9518.S file. cstartup_9518.S: completes the initialization, move and copy of retention_reset, aes_data, retention_data, ramcode, data, sbss, bss, stack, flash initialization, interrupt vector entry, and some functions that need to be written in assembly.
bootlink: Flash, I-RAM, D-RAM layout file.
boot.link: The bootlink corresponding to the _proj_boot_xx project.
boot-36k.link: The bootlink corresponding to the project under vendor, the difference is that the SRAM and the corresponding flash address are offset by 0x9000 (36k).
main.c
It includes the main function entry, the related functions of system initialization, and the writing of the infinite loop while(1). It is recommended not to make any modifications to this file, and directly use inherent writing (some unimportant codes have been omitted).
_attribute_ram_code_ int main(void)
{
sys_init(DCDC_1P4_LDO_1P8, VBAT_MAX_VALUE_GREATER_THAN_3V6);
user_read_flash_value_calib();
clock_init(PLL_CLK_192M, PAD_PLL_DIV, PLL_DIV2_TO_CCLK, CCLK_DIV2_TO_HCLK, HCLK_DIV2_TO_PCLK, PLL_DIV4_TO_MSPI_CLK);
#if WATCHDOG_ENABLE
wd_set_interval_ms(15*1000);
wd_start();
#endif
clock_cal_24m_rc();
clock_32k_init(CLK_32K_RC);
clock_cal_32k_rc(); //6.68ms
user_init();
while(1)
{
main_loop();
#if WATCHDOG_ENABLE
wd_clear_cnt();
#endif
}
return 0;
}
Configuration File
(1) app_config.h
The application function configuration file is used to configure the application function parameters of the whole system, including the configuration of 2.4GHz related functions, USB related functions, audio input functions and algorithm related functions.
(2) app_audio_config.h
The audio function configuration file is used to configure the audio function parameters, including the configuration of the encoder, frame length, gain and other related functions.
(3) app_ui.h
The UI function configuration file is used to configure the UI function parameters, including the configuration of related functions such as key, led, power detection, and tone.
The meaning of each parameter in the configuration file is explained later in the introduction to each module.
BootLoader Introduction
The BootLoader projects including TX and RX, _proj_boot_device_ and _proj_boot_dongle_ respectively. After power-on, it will detect APP firmware integrity and jump to the APP for execution. In the APP firmware, you can also call the function to enter the BootLoader to perform the OTA operation. For the operation method of OTA, please refer to the OTA Tool section.
Telink Ultra Low Latency MIC SDK Introduction
SDK Overview
The Ultra Low Latency MIC supports 2-to-1/1-to-1 format, and has the characteristics of low latency and strong anti-interference, etc. The effective use distance is related to the product format and transmission times, and needs to be tested according to the actual product. Typical 2-to-1 audio performance is as follows:
Analog input and output: latency as low as 8ms (SBC codec) and 12ms (LC3+ codec), frequency response 20 ~ 20kHz, THD+N as low as 0.5%, and SNR reaches 88dB.
IIS input and output: latency as low as 7ms (SBC codec) and 9ms (LC3+ codec), frequency response 20 ~ 20kHz, THD+N as low as 0.04%, and SNR reaches 96dB.
The SDK provides users with two project examples: _proj_ultra_ll_mic_ and _proj_ultra_ll_mic_24bits_, as shown below:
The projects _proj_ultra_ll_mic_ and _proj_ultra_ll_mic_24bits_ not only have the difference between 16bits and 24bits, but also have some differences in algorithm consumption and functionality, as follows.
Project | _proj_ultra_ll_mic_ | _proj_ultra_ll_mic_24bits_ |
---|---|---|
LC3+ encoding time consuming | 852 us | 1037 us |
LC3+ decoding time consuming | 963 us | 1110 us |
USB function | 16bits upstream and downstream | 24bits upstream only |
The 16bits AP metrics are as follows:
The 24bits AP metrics are as follows (where the blue word DUAL_4db is the metric with the MIC_DUAL_INPUT_EN function enabled):
SDK Function Introduction
The specific functions of the TX end are as follows.
(1) Supported application related functions:
a) USB module: USB HID, USB Debug;
b) Watchdog module;
c) Algorithm module: including EQ, noise reduction algorithm, resampling algorithm, DRC algorithm;
d) OTA module;
e) EMI module: support EMI test instructions;
f) 2.4GHz module: RF related: 2.4GHz Tx, transmit power adaptive, frequency hopping; wireless related: manual pairing, reconnection, automatic pairing, pairing ID filtering.
(2) Supported audio related functions:
a) Coding: SBC, LC3+;
b) Audio input: AMIC, DMIC/AMIC/Line-in or I2S. Audio output: I2S or Line-out.
(3) Supported UI related functions:
a) KEY: short press, long press, double click;
b) LED;
c) TTONE: tones;
d) Power detection.
The UI function of the RX end is the same as that of the TX end, other functions are as follows.
(1) Supported application related functions:
a) USB module: USB Audio upstream and downstream, USB HID, USB Debug;
b) Watchdog module;
c) OTA module: OTA upgrade;
d) EMI module: supports EMI fixed frequency test;
e) 2.4GHz module: RF related: 2.4GHz Rx, frequency hopping, packet error correction; wireless related: manual pairing, reconnection, automatic pairing, one-to-one, one-to-two, pairing ID filtering.
(2) Supported audio related functions:
a) Coding: SBC,LC3+;
b) Audio input: DMIC/AMIC/Line-in or I2S. Audio output: I2S or Line-out, USB Audio.
Key Macro Introduction
Many functions of the SDK are controlled via macro switches. The following briefly introduces app_config.h, app_audio_config.h, and app_ui.h.
(1) Application Related Configuration
MIC_TX_NUM:
Default value: 2;
Function description: control the number of Tx;
Remark: the default 2-to-1 mode can be modified to 1-to-1 mode, the time slot becomes shorter and the number of TX retransmissions will increase accordingly.
AUTO_PAIR_EN:
Default value: 0;
Function description: automatic pairing.
TRANSMIT_DATA_EN:
Default value: 0;
Function description: transmit command;
Remark: Transmit bootloader command.
ADAPTIVE_RF_POWER_EN:
Default value: 0;
Function description: transmitting power adaptive adjustment function.
RF_ERROR_CORRECTION_EN:
Default value: 0;
Function description: packet error correction function.
APP_MODE:
Default value: APP_MODE_DEBUG_ONLY;
Function description: USB mode selection;
Remark: default PRINTER debug mode, can be adjusted to USB HID mode as well as USB Audio.
USB_SPEAKER_ENABLE:
Default value: 1;
Function description: USB Audio downstream enable;
Remark: it is enabled by default and can be disabled individually.
USB_MIC_ENABLE:
Default value: 1;
Function description: USB Audio upstream enable;
Remark: it is enabled by default and can be disabled individually.
WATCHDOG_ENABLE:
Default value: 1;
Function description: watchdog enable.
EMI_TEST_ENABLE:
Default value: 0;
Function description: EMI function enable.
APP_NS_ENABLE:
Default value: 0;
Function description: noise cancelling function enable.
APP_WB_NS_ENABLE:
Default value: FS_16K_NS_EN;
Function description: the noise cancelling function uses the default mode, which can be changed to the LD_16K_NS_EN long range noise cancelling mode.
APP_NS_MODE:
Default value: 1;
Function description: the noise cancelling function uses the webrtc algorithm.
APP_EQ_ENABLE:
Default value: 0;
Function description: EQ function enable.
ADC_DRC_EN:
Default value: 0;
Function description: DRC function enable.
PAIR_VPID_FILTER_EN:
Default value: 1;
Function description: the pairing ID filtering function is enabled. During pairing, the manufacturer ID and the product ID can be increased to prevent the pairing of products with different IDs.
UI Related Configuration
LED_ENABLE:
Default value: 1;
Function description: LED enable;
Remark: LED_BLUE, LED_GREEN, LED_WHITE, LED_RED are defined respectively.
KEY_ENABLE:
Default value: 1;
Function description: KEY enable.
TTONE_EN:
Default value: 1;
Function description: tone enable;
Remark: to play the tone, the tone bin file needs to be burned.
ENABLE_BATT_CHECK:
Default value: 0;
Function description: power detection enable;
Remark: the PB3 pin is used by default and can be modified at the initialization of the bat_check_init interface.
(3) Audio Related Configuration
MIC_DUAL_INPUT_EN:
Default value: 0;
Function description: when turned on, the PCM data of the left and right mic will be fused to obtain better dynamic input, SNR, bottom noise and other parameters.
Remark: it is necessary to merge the input of the left mic with the right mic on the hardware to achieve two input channels.
CODEC_DAC_MONO_MODE:
Default value: 1;
Function description: 1: mono, 0: stereo;
Remark: the Lineout on the Tx end is used to monitor input audio, it is enabled by default and can be disabled.
CODEC_ALGORITHM_SEL:
Default value: CODEC_ALGORITHM_LC3A;
Function description: codec selection;
Remark: LC3+ encoding is selected by default, and SBC encoding can be selected.
LC3A_BIT_RATE:
Default value: 96000(72000);
Function description: LC3+ bit rate;
Remark: LC3+ uses 96k bit rate at 120 Samples (72k for proj_ultra_ll_mic_240 project).
MIC_SAMPLING_RATE:
Default value: 48000;
Function description: mic sample rate;
Remark: default 48k.
MIC_SAMPLES_PER_PACKET:
Default value: 120 (240 for proj_ultra_ll_mic_240 project);
Function description: mic sample;
Remark: the default sample is 2.5ms (5ms), so mic one sample = 2.5(5)*48000/1000= 120(240).
MIC_PACKET_NUM:
Default value: 8(14);
Function description: the number of times that Tx sends audio packets;
Remark: In the case of 1-to-2 (discussed separately for 1-to-1), the Tx end retransmits 8/2 = 4 times per packet of audio data, and the specific value is affected by the time slot and coding.
AUDIO_INTERFACE:
Default value: AUDIO_I2S_CODEC;
Function description: codec audio source selection;
Remark: the default is the internal codec input and output. If it is changed to AUDIO_I2S_GPIO, it will be switched to I2S input and output.
AUDIO_IN_MODE:
Default value: LINE_INPUT;
Function description: Internal codec input source selection;
Remark: when AUDIO_INTERFACE is configured as AUDIO_I2S_CODEC, internal codec input source selection can be performed with this macro, supporting AMIC, DMIC, Linein.
AUDIO_INTERFACE_ROLE:
Default value: AUDIO_I2S_AS_MASTER;
Function description: external pin input source master-slave selection;
Remark: when AUDIO_INTERFACE is configured as AUDIO_I2S_GPIO, the I2S role can be configured as master/slave through this macro.
MIC_AB_EN:
Default value: 0;
Function description: A mic and B mic functions enable.
MIX_ADC_AUDIO_EN:
Default value: 0;
Function description: Rx end mixed local mic function enable;
Remark: Rx end.
CODEC_IN_A_GAIN:
Default value: CODEC_IN_A_GAIN_0_DB;
Function description: Analogue input gain;
Remark: Used to configure the analogue input gain on the Tx-terminal and Rx-terminal.
CODEC_IN_D_GAIN:
Default value: CODEC_IN_D_GAIN_0_DB;
Function description: Digital input gain;
Remark: Used to configure the digital input gain on the Tx-terminal and Rx-terminal.
CODEC_OUT_A_GAIN:
Default value: CODEC_OUT_A_GAIN_0_DB;
Function description: analog output gain;
Remark: Used to configure analog output gain of Tx end and Rx end.
CODEC_OUT_D_GAIN:
Default value: CODEC_OUT_D_GAIN_0_DB;
Function description: digital output gain;
Remark: Used to configure digital output gain of Tx end and Rx end.
Audio Related
Sampled Audio Basic Concepts
Sampling: Take a point at a certain time interval for the analog signal.
Quantification: Add a scale to the vertical coordinate, and take an integer value according to the approximation, so that the values 鈥嬧€媜f the obtained sampling points are all integers.
Coding: The integer value obtained by quantization is encoded in binary.
Digital signal: Turn the encoded sequence of 0 and 1 into high and low level signals.
Pulse code modulation, or PCM for short. It can be seen from the above analog-to-digital conversion that the content stored in the PCM format file is actually the sequence obtained by encoding.
Sampling frequency: Sampling is to digitize the analog signal on the time axis, According to the Nyquist theorem (sampling theorem), sampling is performed at a frequency that is more than 2 times the highest frequency of the sound (AD conversion). Sounds with frequencies between 20Hz and 20kHz can be recognized by the human ear, so the sampling frequency is generally around 40kHz, the commonly used music is 44.1kHz (44100 times/s sampling), 48kHz, etc. The sampling rate for telephones is 8kHz.
Sampling bits: The range of data that each sample point can represent. The number of sampling bits is usually 8 bits or 16 bits, the larger the number of sampling bits, the more delicate the change of the sound that can be recorded, and the larger the corresponding amount of data. 16 bits is the most common sampling accuracy.
Number of channels: The number of channels refers to the number of speakers that can support different sounds, the number of channels commonly used are mono, stereo (left channel and right channel).
PCM Format
PCM is uncompressed audio data after sampling and quantization, which is standard digital audio data converted from analog signals through sampling, quantization, and encoding. If it is a mono audio file, the sampled data is stored in the order of time (if it 's a double channel, store it in LRLR mode), and the storage is also related to the big-endian/little-endian of the machine. The little-endian mode is shown below.
Encode and Decode
Supported codec: SBC, LC3Plus, ADPCM.
The SBC and LC3Plus are used for audio compression and decompression for air transmission, ADPCM decoding speed is fast, used to play the tone.
MCU/SDK Related Functions
Flash Address Space Allocation
The Flash storage information takes the size of one sector (4K bytes) as the basic unit, because the erase of Flash is based on sector (the erase function is flash_erase_sector). In theory, the same type of information needs to be stored in one sector, and different types of information need to be stored in different sectors (to prevent other types of information from being erased by mistake when erasing information). Therefore, it is recommended that users follow the principle of "put different types of information in different sectors" when using Flash to store customized information.
The chip supports 1MB of Flash as the program storage space by default. The SDK defines "FLASH_SIZE" as 1MB at the end of the file boot.link, and makes a limit judgment on "BIN_SIZE" <= "FLASH_SIZE". If the user uses Flash larger than 1MB, the description needs to be modified accordingly.
Flash is divided into Locked area and Unlocked area according to function, which respectively store data that does not need to be changed and data that can be changed at any time. Data that will be changed, such as pairing information, is stored in the Unlocked area, while data that will not be changed, such as firmware, version information, USB_ID, tone, DRC parameters, and EQ parameters, are stored in the Locked area.
(1) The sector 0xFF000 ~ 0xFFFFF stores the MAC address, in fact the 6 bytes of MAC address are stored in 0xFF000 ~ 0xFF005, the high byte address is stored at 0xFF005 and the low byte address is stored at 0xFF000. For example, if the contents of Flash 0xFF000 ~ 0xFF005 are 0x11 0x22 0x33 0x44 0x55 0x66 in order, then the MAC address is 0x665544332211.
Telink's mass production jig system will burn the MAC address of the actual product to the address 0xFF000, which corresponds to the SDK. If the user needs to modify this address, please make sure that the address burned by the jig system is also modified accordingly. In the SDK, the user_init function will read the MAC address from the CFG_ADR_MAC_1M_FLASH address of the Flash, this macro can be modified in /vendor/common/blt_common.h.
#ifndef CFG_ADR_MAC_1M_FLASH
#define CFG_ADR_MAC_1M_FLASH 0xFF000
#endif
(2) The two sectors 0xEA000 and 0xEB000 are occupied by the 2.4GHz protocol stack and are used to store pairing information.
(3) 0x00000 ~ 0x3FFFF 256KB space is used as program space by default. 0x00000 ~ 0x3FFFF total 256KB is firmware storage space. 0x40000 ~ 0x7FFFF 256KB is the space for storing new firmware during OTA update, that is, the supported firmware space is theoretically 256KB. For some special reason, the 0x40000 ~ 0x7FFFF high address space can actually only be used for 254KB, and the last 4KB cannot be used.
Note:
In fact the last 4KB of all high address spaces cannot be used.
(4) 0xDF000 is the MAC address for the mass production, and we use this address as MAC address by default. If the address does not store data, the address where 0xFF000 is will be read.
(5) 0xDC000 is the EQ data storage address.
(6) 0xDA000 is the USB PID VID storage address.
(7) 0xD9000 is the DRC algorithm data storage address.
(8) 0x80000 is the tone storage address.
(9) 0xFFFF0 is the USB_ID storage address.
(10) The remaining Flash space is all used as the user's data storage space.
Flash Management Function
When users need to develop and frequently read and write Flash-related functions, such as volume, shutdown time, etc. (SDK pairing information), it is recommended to use the Flash management function provided by the SDK, which can open a section of the specified address dedicated to the management of user-defined Flash content, and can achieve the following advantages:
(1) Flash management: Write from the beginning at the address of user-specified area, when detecting the end of Flash, it will erase the area and continue to write from the beginning, and the cycle is repeated to increase the service life of Flash as well as the utilization rate, and to reduce the Flash erase action.
(2) Same data will not be written: When calling the write Flash interface of Flash management module, it will compare the written content in the Flash, and if it is the same, it will not be written.
(3) Data verification: Before writing data, the data will be verified, and if the verification fails, the data will not be written, so as to prevent writing wrong data.
(4) Data backup function: When FLASH_STORAGE_BACKUP_EN is on (it is off by default), half of the specified Flash area will be used for backup, to prevent data loss problems caused by power down when Flash is erased and ready to be written.
Introduction to the main functions of Flash management functions
flash_storage_init (flash_storage_t *fs, u32 addr, u8 *item_ptr, u16 item_size)锛歩nitialization function for flash read/write function. In addition to initializing the flash read/write function, it will also read the flash data into a custom data structure. The first parameter is the address of a structure related to this function, the second parameter is the address of the flash to be read and written, the third parameter is the address of the customized data structure, and the fourth parameter is the length of the customized data structure.
flash_storage_write (flash_storage_t *fs)锛歵he function interface to write flash. Called to write data from a custom structure to flash. The argument is the address of a structure associated with this function.
flash_storage_read (flash_storage_t *fs)锛歵he function interface for reading flash. Since flash_storage_init call will call this function once, i.e., it will read the flash data into the custom data structure when initializing. So this function is not called in general.
Operation flow of flash management function
The above is the introduction of flash read/write function, then take the SDK pairing information as an example to introduce how to operate.
(1) First of all, define a pairing information flash_storage_t structure (global variable) fs_pair_info. Next, define the pairing information pair_info_s structure (divided into TX and RX) pair_info, as follows:
typedef struct {
u32 access_code;
#if APP_BUILD == APP_MIC_RX
u32 slave_ids[REMOTE_DEVICE_NUM_MAX]; //<! for RX
u8 remote_mac[REMOTE_DEVICE_NUM_MAX][LOCAL_MAC_LEN]; //<! for RX
u8 lp_dev_id; //<! for RX, last paired dev id.
#else
u8 remote_mac[LOCAL_MAC_LEN]; // for TX
u8 dev_id;
#endif
u8 connect_chn;
u8 valid;
} pair_info_s;
pair_info_s pair_info;
flash_storage_t fs_pair_info;
(2) Call flash_storage_init (four-byte alignment) in the app_user_data_init interface for fs_pair_info initialization and pair_info binding as follows:
void app_user_data_init(void)
{
......
flash_storage_init(&fs_pair_info, FLASH_SYS_USER_BASE, (u8 *)&pair_info, sizeof(pair_info));
if (!pair_info.valid) {
tmemset(&pair_info, 0, sizeof(pair_info));
}
......
}
This step reads the pairing information stored in the Flash at the address corresponding to FLASH_SYS_USER_BASE into pair_info (when initialized for the first time, the contents of the Flash are all FF, and since the valid value in the pair_info_s structure of pair_info_s is not legal, all the values in pair_info are set to 0).
(3) When TX and RX complete the pairing process, regardless of TX or RX, pair_info will be updated accordingly, and finally call flash_storage_write in the app_user_data_update interface to write the updated initial value of the pair_info with the header of flash_storage_header_s and then write it to the Flash, as shown below:
typedef struct {
u16 version; /**< Version of data. */
u16 length; /**< Length of data, exclude header(version, length, verify). */
u32 verify; /**< Verify of data. */
} flash_storage_header_s;
void app_user_data_update(void) // user data information changed, call this func write to flash
{
pair_info.valid = PAIR_INFO_VALID_BYTE;
flash_storage_write(&fs_pair_info); /* push buffer into flash */
}
(4) Take the pairing process of TX0 (mac address: 22 11 22 33 44 55), TX1 (mac address: 11 11 22 33 44 55) and RX (mac address: 33 11 22 33 44 55) as example:
After TX0 and RX are paired for the first time, the contents of the 0xEB000 address are read through the risc-v tool respectively as follows.
In TX0 pairing information:
- Bytes 0~1: version in flash_storage_header_s, refers to the data version number, currently undefined, default is 0x0000.
- Bytes 2~3: length in flash_storage_header_s, refers to the length of the pairing information stored by TX0 as 0x0010, which is 16 bytes.
- Bytes 4~7: 4 bytes verify in flash_storage_header_s, refers to the checksum value of 0x000003ab for the whole stored information.
- Bytes 8~11: access_code value is 0x4f4e2cfb.
- Bytes 12~17: remote_mac, refers to the corresponding mac address of the paired RX device as 33 11 22 33 44 55.
- Byte 18: dev_id value of 0x00, refers to TX0 current device id.
- Byte 19: connect_chn value is 0x08, referring to the sequence value corresponding to the paired channel.
- Byte 20: valid value is 0xad, legal.
- Bytes 21~23: for four-byte alignment.
In RX pairing information:
- Bytes 0~1: version in flash_storage_header_s, refers to the data version number, currently undefined, default is 0x0000.
- Bytes 2~3: length in flash_storage_header_s, refers to the length of the pairing information stored by Rx as 0x001c, which is 28 bytes.
- Bytes 4~7: 4 bytes verify in flash_storage_header_s, refers to the checksum value of 0x00000466 for the whole stored information.
- Bytes 8~11: access_code with value 0x4f4e2cfb.
- Bytes 12~15: slave_ids of TX0, refers to the corresponding slave id of the paired TX0 device as 0x44662200.
- Bytes 16~19: slave_ids of TX1, refers to the slave id corresponding to the paired TX1 device, the value is 0x00000000 since it is unpaired.
- Bytes 20~25: remote_mac of TX0, refers to the mac address corresponding to the paired TX0 device as 22 11 22 33 44 55.
- Bytes 26~31: remote_mac of TX1, refers to the mac address corresponding to the paired TX1 device, the value is 0x00000000 since it is unpaired.
- Byte 32: lp_dev_id, refers to the last paired device id of 0x00 (the device id of TX0 is stored here).
- Byte 33: connect_chn value of 0x08, refers to the sequence value corresponding to the paired channel.
- Byte 34: valid value is 0xad, legal.
- Byte 35: for four-byte alignment.
On the basis that TX0 and RX have been paired, the pairing process of TX1 and RX is carried out. The RX pairing information pair_info will carry out the updating of the pairing information in the Flash due to the addition of the Tx1-related information, i.e., on the basis of the pairing information corresponding to the previous TX0, it will be shifted to the next address, and the content of the address of 0xEB000 is read through the risc-v tool respectively, as follows.
In TX1 pairing information:
- Bytes 0~1: version, default is 0x0000.
- Bytes 2~3: length, refers to the length of the pairing information stored by TX1 is 0x0010, which is 16 bytes.
- Bytes 4~7: verify, 0x000003ac.
- Bytes 8~11: access_code value is 0x4f4e2cfb.
- Bytes 12~17: remote_mac, refers to the corresponding mac address of the paired RX device as 33 11 22 33 44 55.
- Byte 18: dev_id value is 0x01, refers to the current device id of TX1.
- Byte 19: connect_chn value is 0x08, refers to the sequence value corresponding to the paired channel.
- Byte 20: valid value is 0xad, legal.
- Bytes 21~23: for four-byte alignment.
In RX pairing information:
- Bytes 36~37: version, default is 0x0000.
- Bytes 38~39: length, refers to the length of the Rx stored pairing information is 0x001c, which is 28 bytes.
- Bytes 40~43: verify, refers to the checksum value of 0x00000665 for the whole stored information.
- Bytes 44~47: access_code, with a value of 0x4f4e2cfb.
- Bytes 48~51: slave_ids of TX0, refers to the corresponding slave id of the paired TX0 device as 0x44662200.
- Bytes 52~55: slave_ids of TX1, refers to the slave id of the paired TX1 device as 0x44552233.
- Bytes 56~61: remote_mac of TX0, refers to the corresponding mac address of the paired TX0 device as 22 11 22 33 44 55.
- Bytes 62~67: remote_mac of TX1, refers to the corresponding mac address of the paired TX1 device as 11 11 22 33 44 55.
- Byte 68: lp_dev_id, refers to the last paired device id of 0x01 (the device id of TX1 is stored here).
- Byte 69: connect_chn value of 0x08, refers to the sequence value corresponding to the paired channel.
- Byte 70: valid value is 0xad, legal.
- Byte 71: for four-byte alignment.
Note:
The flash_storage_t structure fs_pair_info is bound to the pairing information structure pair_info after initialization. It is the pair_info that operates on the data, and it is only when the flash_storage_write interface is called after a change in the pair_info (comparing the information stored in Flash) that the flash_storage_t structure fs_pair_info undergoes an address shift and eventually writes the updated pair_info to the offset address in Flash.
Wireless Audio Transmission
In the SDK application, using the LE 2M Phy, the physical layer transmission rate of 2M bit/s, that is, the time of one byte transmission is 4us. Understanding LinkLayer users need to have a certain understanding of Telink's RF driver, due to the RF peripheral content is more, this chapter only outlines the SDK used in the RF interface function, users can refer to the driver handbook via Telink's wiki website for other functions.
Audio stream transmission process
The entire audio data stream is the broadcast mechanism, TX will continue to send audio packets, RX received without the need to respond ack, different configurations of different modes will use 2 / 2.5 / 5ms for a frame of data, in each frame data it is divided into N slot interval for sending and receiving packets, for different project example the configuration of the N value is different, such as the proj_ultra_ll_mic project uses LC3+ coding to configure N as 9 (slot 0 ~ slot 8). When coding and decoding, RX will decode the audio packet received in front at a fixed slot, and TX will encode the current audio packet at a fixed slot, which ensures that the transmission delay of the whole audio data stream remains unchanged.
(1) 2.5ms frame length, LC3+, 2-to-1 mode
1 frame is 2.5ms, and 9 more slot intervals are divided in each frame for sending and receiving packets.
Among them, slot 0 is the synchronization channel, which is used for RX transmission of TX hopping table, pairing/reconnection, other control commands and other information.
Slot 1 ~ slot 4 are TX0 packet sending slots.
Slot 5 ~ slot 8 are TX1 packet sending slots.
As shown in the figure, in 1 frame, TX0 finishes encoding before slot 0, then starts from slot 1 to slot 4, sends the encoded audio packet 4 times consecutively, and RX receives the packet 4 times consecutively, followed by decoding at slot 5.
And TX1 finishes encoding before slot 4, then sends 4 consecutive encoded audio packets starting from slot 5 to slot 8, RX receives 4 consecutive packets, and then decodes at slot 0 in the next frame.
(2) 2.5ms frame length, LC3+, 1-to-1 mode
1 frame is 2.5ms, and in each frame there are 9 more slot intervals for sending and receiving packets.
Slot 0 is the same as 2-to-1 mode.
Slot 1 ~ slot 8 are TX packet sending slots.
As shown in the figure, in 1 frame, TX finishes encoding before slot 0, then sends the encoded audio packet 8 times consecutively starting from slot 1 to slot 8, and RX receives the packet 8 times consecutively, followed by decoding at slot 0 in the next frame.
Note:
Compared to the 2-to-1 mode, in the 1-to-1 mode, the number of TX retransmissions is 4 times more because there is only 1 transmitter, but the decoding position is adjusted to the decoding position of TX1 in the 2-to-1 mode, so the transmission distance becomes farther while the latency is increased by about 1.25ms.
RF Main Function Description
async_init: Initialize system timer, initialize RF, frequency hopping, timing parameters and various interrupts;
async_stx_start: RF packet sending function;
async_srx_start: RF packet receiving function;
async_irq_rf: RF interrupt handling function.
Scan Connection Process
Pairing Process
Low Power Management
The MCU is in working mode during normal program execution, and the working current is between 3 ~ 7mA. If you need to save power, the chip needs to enter the low power mode.
Low power mode, also known as sleep mode, includes three types: suspend mode, deepsleep mode, and deepsleep retention mode.
Deepsleep mode
The SDK currently supports deepsleep mode, at this time, the program stops running, most of the hardware modules of the MCU are powered down, while the PM hardware modules remain operational. In deepsleep mode the IC current is less than 1uA, if the standby current of the built-in flash is around 1uA, this may result in a measured deepsleep current of 1 ~ 2uA. When deepsleep mode wakes up, the MCU will restart, similar to the effect of power on, and the program will restart to initialize.
In Deepsleep mode, except for a few registers on the analog register that can save the state, all other SRAM, digital register, and analog register lose state when powered down.
The PM module special non-power-down analog regsiter, DEEP_ANA_REG in the drivers/B91/pm.h file, as shown in the following code.
#define PM_ANA_REG_POWER_ON_CLR_BUF0 0x39 // initial value 0x00. [Bit0][Bit1] is already occupied. The customer cannot change!
#define PM_ANA_REG_POWER_ON_CLR_BUF1 0x3a // initial value 0x00
#define PM_ANA_REG_POWER_ON_CLR_BUF2 0x3b // initial value 0x00
#define PM_ANA_REG_POWER_ON_CLR_BUF3 0x3c // initial value 0x00
#define PM_ANA_REG_POWER_ON_CLR_BUF4 0x3d // initial value 0x00
#define PM_ANA_REG_POWER_ON_CLR_BUF5 0x3e // initial value 0x00
#define PM_ANA_REG_POWER_ON_CLR_BUF6 0x3f // initial value 0x0f
#define PM_ANA_REG_POWER_ON_CLR_BUF7 0x38 //initial value =0xff
The registers above 0x3f will only restore the initial value when powered down. It should be noted that customers are not allowed to use ana_39, this analog register is reserved for use by the underlying stack, if the application layer code uses this register, it needs to be modified to ana_3a ~ ana_3f. Because the number of non-power-down analog registers is relatively small, it is recommended that customers use each bit to indicate different status bit information.
The 0x38 register will be initialized in the three cases: hardware/software reset, power down and watchdog. It should be noted that the bit0 bit is already used by stack, and users need to avoid this bit when using it.
The user can use the return value of the API pm_get_mcu_status(void) after sys_init(power_mode_e power_mode) to determine which state the cpu returns from, the return value is as follows:
typedef enum{
MCU_STATUS_POWER_ON = BIT(0),
MCU_STATUS_REBOOT_BACK = BIT(2), //the user will not see the reboot status.
MCU_STATUS_DEEPRET_BACK = BIT(3),
MCU_STATUS_DEEP_BACK = BIT(4),
MCU_STATUS_REBOOT_DEEP_BACK = BIT(5), //reboot + deep
}pm_mcu_status;
Low Power Wake-up Source
The suspend/deepsleep/deepsleep retention can be woken up by GPIO PAD and timer. This SDK only focuses on 2 wake-up sources, as shown below (Note that the two definitions of PM_TIM_RECOVER_START and PM_TIM_RECOVER_END in the code are not wake-up sources).
typedef enum {
Telink B91 BLE Single Connection SDK Developer Handbook
AN-20111001-C2 217 Ver.0.2.0
PM_WAKEUP_PAD = BIT(3),
PM_WAKEUP_TIMER = BIT(5),
}SleepWakeupSrc_TypeDef;
As shown in the figure above, deepsleep mode has 2 wake-up sources in hardware: timer and GPIO PAD.
The wake-up source PM_WAKEUP_TIMER comes from hardware 32k timer (32k RC timer or 32k Crystal timer). The 32k timer has been correctly initialized in the SDK, and the user does not need any configuration when using it, just set the wake-up source in cpu_sleep_wakeup().
The wake-up source PM_WAKEUP_PAD comes from the GPIO module, and the high and low levels of all GPIOs (PAx/PBx/PCx/PDx/PEx) except the 4 pins of MSPI have wake-up function.
The API for configuring GPIO PAD to wake up sleep mode:
typedef enum{
WAKEUP_LEVEL_LOW = 0,
WAKEUP_LEVEL_HIGH = 1,
}pm_gpio_wakeup_level_e;
void pm_set_gpio_wakeup (gpio_pin_e pin, pm_gpio_wakeup_Level_e pol, int en);
#define cpu_set_gpio_wakeup pm_set_gpio_wakeup
pin is defined for GPIO.
pol is the definition of wake-up polarity: Level_High means high level wake-up, Level_Low means low level wake-up.
en: 1 means enable, 0 means disable.
For example:
cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 1); //GPIO_PC2 PAD wake-up on, high level wake-up
cpu_set_gpio_wakeup (GPIO_PC2, Level_High, 0); //GPIO_PC2 PAD wake-up off
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 1); //GPIO_PB5 PAD wake-up on, low level wake-up
cpu_set_gpio_wakeup (GPIO_PB5, Level_Low, 0); //GPIO_PB5 PAD wake-up off
In the ui definition of EVB, in the app_key.c file's app_key_scan_init interface, call pm_set_gpio_wakeup to configure all the keys' corresponding pins to be wakeup on, high level wakeup:
void app_key_scan_init(void)
{
......
for(int i=0;i<MATRIX_ROW_COUNT;i++) {
gpio_pin_e pin = matrix_row_pins[i];
gpio_set_input_en(pin, 1);
gpio_set_output_en(pin, 0);
gpio_setup_up_down_resistor(pin, PM_PIN_PULLDOWN_100K);
pm_set_gpio_wakeup(pin, WAKEUP_LEVEL_HIGH, 1);
}
......
}
Low Power Mode Entry and Wake-up
The API to set the MCU to go to sleep and wake up:
typedef int (*cpu_pm_handler_t)(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, unsigned int
wakeup_tick);
cpu_pm_handler_t cpu_sleep_wakeup;
The first parameter sleep_mode: used to set the sleep mode, there are the following 4 options, namely suspend mode, deepsleep mode, deepsleep retention 32K Sram, deepsleep retention 64K Sram.
typedef enum {
//available mode for customer
SUSPEND_MODE = 0x00,
DEEPSLEEP_MODE = 0x30,
DEEPSLEEP_MODE_RET_SRAM_LOW32K = 0x21, //for boot from sram
DEEPSLEEP_MODE_RET_SRAM_LOW64K = 0x03, //for boot from sram
DEEPSLEEP_MODE_RET_SRAM = 0x21,
//not available mode
DEEPSLEEP_RETENTION_FLAG = 0x0F,
}SleepMode_TypeDef;
The second parameter wakeup_src: set the wakeup source for the current suspend/deepsleep, and the parameter can only be one or more of PM_WAKEUP_PAD and PM_WAKEUP_TIMER. If wakeup_src is 0, then it cannot be woken up after entering low power sleep mode.
The third parameter wakeup_tick: when PM_WAKEUP_TIMER is set in wakeup_src, wakeup_tick needs to be set to determine when the timer wakes up the MCU, if PM_WAKEUP_TIMER is not set to wake up, this parameter is meaningless. The value of wakeup_tick is an absolute value, which is set according to the System Timer tick introduced earlier in this document, when the value of the System Timer tick reaches the set wakeup_tick, sleep mode is woken up. The value of wakeup_tick needs to be based on the value of the current System Timer tick and the absolute time converted from the time to sleep to effectively control the sleep time. If the wakeup_tick is set directly without considering the current System Timer tick, the wakeup time cannot be controlled. Since wakeup_tick is an absolute time, it must be within the range that the 32bit System Timer tick can represent, so the maximum sleep time that this API can represent is limited. The current design is that the maximum sleep time is 7/8 of the corresponding time of the maximum System Timer tick represented by 32 bits. The maximum System Timer tick can represent about 268s, then the longest sleep time is 268 * 7/8 = 234s, that is, the following delta_Tick cannot exceed 234s. If a longer sleep time is required, the user can call the long sleep function.
cpu_sleep_wakeup(SUSPEND_MODE, PM_WAKEUP_TIMER, clock_time() + delta_tick);
The return value is the set of wake-up sources for the current sleep mode, and the wake-up sources corresponding to each bit of the return value are:
typedef enum {
WAKEUP_STATUS_COMPARATOR = BIT(0),
WAKEUP_STATUS_TIMER = BIT(1),
WAKEUP_STATUS_CORE = BIT(2),
WAKEUP_STATUS_PAD = BIT(3),
WAKEUP_STATUS_MDEC = BIT(4),
STATUS_GPIO_ERR_NO_ENTER_PM = BIT(7),
STATUS_ENTER_SUSPEND = BIT(30),
}pm_wakeup_status_e;
a) The bit of WAKEUP_STATUS_TIMER is 1, indicating that the current sleep mode is awakened by Timer.
b) The bit of WAKEUP_STATUS_PAD is 1, indicating that the current sleep mode is awakened by GPIO PAD.
c) When WAKEUP_STATUS_TIMER and WAKEUP_STATUS_PAD are 1 at the same time, it means that the two wake-up sources of Timer and GPIO PAD are in effect at the same time.
d) STATUS_GPIO_ERR_NO_ENTER_PM is a special state indicating that a GPIO wake-up error has currently occurred: For example, when a GPIO PAD is set to wake up at a high level, try to call cpu_sleep_wakeup to enter suspend when the GPIO is at a high level, and set the PM_WAKEUP_PAD wake-up source. At this time, it will fail to enter suspend, the MCU immediately exits the cpu_sleep_wakeup function, and returns value STATUS_GPIO_ERR_NO_ENTER_PM.
Use the following forms to control sleep time:
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_TIMER, clock_time() + delta_Tick);
The delta_Tick is a relative time (such as 100* CLOCK_16M_SYS_TIMER_CLK_1MS), plus the current clock_time() it becomes an absolute time.
Example of cpu_sleep_wakeup usage:
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_PAD, 0);
When the program executes this function, it enters suspend mode and can only be woken up by GPIO PAD.
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_TIMER, clock_time() + 10* CLOCK_16M_SYS_TIMER_CLK_1MS)锛�
When the program executes this function, it enters suspend mode and can only be woken up by Timer. The wake-up time is the current time plus 10ms, so the suspend time is 10ms.
cpu_sleep_wakeup (SUSPEND_MODE , PM_WAKEUP_PAD | PM_WAKEUP_TIMER,clock_time() + 50* CLOCK_16M_SYS_TIMER_CLK_1MS);
When the program executes this function, it enters suspend mode and can be woken up by GPIO PAD and Timer. The wake-up time of Timer is set to 50ms. If the GPIO wake-up operation is triggered before the end of 50ms, the MCU will be woken up by the GPIO PAD; if there is no GPIO operation within 50ms, the MCU will be woken up by the Timer.
cpu_sleep_wakeup (DEEPSLEEP_MODE, PM_WAKEUP_PAD, 0);
When the program executes this function, it enters deepsleep mode and can be woken up by GPIO PAD.
cpu_sleep_wakeup (DEEPSLEEP_MODE_RET_SRAM_LOW32K , PM_WAKEUP_TIMER, clock_time() + 8*
CLOCK_16M_SYS_TIMER_CLK_1S);
When the program executes this function, it enters deepsleep retention 32K Sram mode and can be woken up by the Timer, and the wake-up time is 8s after the function is executed.
cpu_sleep_wakeup (DEEPSLEEP_MODE_RET_SRAM_LOW32K , PM_WAKEUP_PAD | PM_WAKEUP_TIMER,clock_time() + 10*
CLOCK_16M_SYS_TIMER_CLK_1S);
When the program executes this function, it enters deepsleep retention 32K Sram mode, which can be woken up by GPIO PAD and Timer, and the timer wakeup time is 10s after the function is executed. If the GPIO wake-up operation is triggered before the end of 10s, the MCU will be woken up by the GPIO PAD; if there is no GPIO operation within 10s, the MCU will be woken up by the Timer.
Running Flow after Low Power Wake-up
When the user calls the API cpu_sleep_wakeup, the MCU enters the sleep mode; when the wake-up source triggers the MCU to wake up, the software running process of the MCU is inconsistent for different sleep modes.
The following details the MCU running flow after deepsleep is woken up. Please refer to the diagram below.
After the MCU is powered on, the introduction of each process:
(1) Run hardware bootloader
The MCU hardware performs some fixed operations, which are solidified on the hardware and cannot be modified by software.
A few examples to illustrate these operations, such as: chip power-on/deep wake-up: by reading the Flash boot and marking "TLNK", determine the current firmware storage address that should be running (offset address 0x00000/0x20000/0x40000/0x80000), then jump to the corresponding address of Flash (base address 0x20000000 + offset address 0x00000/0x20000/0x40000/0x80000) to start executing the software bootloader.
(2) Run software bootloader
After the hardware bootloader finishes running, the MCU starts to run the software bootloader. Software bootloader is the vector end introduced earlier (corresponding to the assembler program in cstartup_B91.S).
Software bootloader is to set the memory environment for the running of the C language program, which can be understood as the initialization of the entire memory.
(3) System initialization
System initialization corresponds to the initialization of each hardware module (including sys_init, rf_drv_init, gpio_init, clock_init) between sys_init and user_init in the main function, which is used to set the digital/analog register status of each hardware module.
(4) User initialization
User initialization corresponds to the function user_init in the SDK.
(5) main_loop
After the user initialization is complete, it enters the main_loop controlled by while(1). A series of operations before entering sleep mode in main_loop is called "Operation Set A", and a series of operations after sleep wakeup is called "Operation Set B".
If the cpu_sleep_wakeup function is called to enter deepsleep mode, when deepsleep is woken up, the MCU will return to Run hardware bootloader again.
The process of deepsleep wake_up is almost the same as that of Power on, and all the hardware and software initialisation has to be done again.
After the MCU enters deepsleep, all Sram and digital/analog registers (except a few analog registers) will be powered down, so the power consumption is very low, and the MCU current is less than 1uA.
Low Battery Detection
This document is uniformly described under the name "low battery detection".
The Importance of Low Battery Detection
For battery powered products, since the battery power will gradually decrease, when the voltage drops to a certain value, it will cause many problems:
(1) The working voltage range of B91 chip is 1.8V ~ 4.3v. When the voltage is lower than 1.8V, stable operation cannot be guaranteed.
(2) When the battery voltage is low, due to the instability of the power supply, the "write" and "erase" operations of the Flash may have the risk of errors, resulting in program firmware and user data being abnormally modified, which eventually leads to product failure. Based on previous mass production experience, we set the low voltage threshold that may present risks as 2.0V.
According to the above description, a secure voltage must be set for battery-powered products, only when the voltage is higher than this secure voltage can the MCU continue to work; once the voltage is lower than the secure voltage, the MCU stops running and needs to be shut down immediately (use to enter deepsleep mode in the SDK to achieve).
The secure voltage is also known as the alarm voltage, and this voltage value is currently used by default in the SDK at 2.2V. If the user has an unreasonable design in the hardware circuit that causes the stability of the power supply network to deteriorate, the secure voltage value will need to continue to be increased, e.g. 2.3V, 2.4V, etc.
For the developed product, as long as the battery is used for power supply, the low battery detection must be a task that runs in real time throughout the life cycle of the product to ensure product stability.
Implementation of Low Battery Detection
Low battery detection requires the use of an ADC to measure the supply voltage. User please refer to the ADC section of the "Driver SDK Developer Handbook" to gain the necessary understanding of the ADC module.
The implementation of low battery detection is described in combination with the implementation given by the SDK project example "proj_ultra_ll_mic_tx", refer to the files bat.c and bat.h.
It must be ensured that the macro "ENABLE_BATT_CHECK" in the app_config.h file is enabled, this macro is disabled by default, users need to pay attention when using the low battery detection function.
#define ENABLE_BATT_CHECK 1
Considerations for Low Battery Detection
Low battery detection is a basic ADC sampling task. When implementing ADC to sample the power supply voltage, there are some issues that need to be paid attention to, which are explained as follows.
(1) Recommended GPIO Input Channel
There are two sampling methods, which can be sampled by Vbat or GPIO analog signal input, but the Vbat channel sampling accuracy is poor, it is recommended to sample through external GPIO method in occasions that require high sampling accuracy.
The available GPIO input channels are the input channels corresponding to PB0~PB7, PD0, and PD1.
typedef enum{
ADC_GPIO_PB0 = GPIO_PB0 | (0x1<<12),
ADC_GPIO_PB1 = GPIO_PB1 | (0x2<<12),
ADC_GPIO_PB2 = GPIO_PB2 | (0x3<<12),
ADC_GPIO_PB3 = GPIO_PB3 | (0x4<<12),
ADC_GPIO_PB4 = GPIO_PB4 | (0x5<<12),
ADC_GPIO_PB5 = GPIO_PB5 | (0x6<<12),
ADC_GPIO_PB6 = GPIO_PB6 | (0x7<<12),
ADC_GPIO_PB7 = GPIO_PB7 | (0x8<<12),
ADC_GPIO_PD0 = GPIO_PD0 | (0x9<<12),
ADC_GPIO_PD1 = GPIO_PD1 | (0xa<<12),
}adc_input_pin_def_e;
The GPIO input channel is used for ADC sampling of the power supply voltage, the specific usage is as follows: in the hardware circuit design, the power supply is directly connected to the GPIO input channel. When the ADC is initialized, set GPIO to high impedance state (ie, oe, and output are all set to 0). At this time, the voltage of the GPIO is equal to the power supply voltage, and the ADC sampling can be performed directly.
During initialization, all states (ie, oe, output) can use the default state without special modification, PB3 is selected as the GPIO input channel by default in the project example.
(2) Only Use Differential Mode
Although the ADC input mode supports both single ended mode and differential mode, for some specific reasons, Telink stipulates that only differential mode can be used, and single ended mode is not allowed.
The input channel of differential mode is divided into positive input channel and negative input channel, and the measured voltage value is the voltage difference obtained by subtracting the negative input channel voltage from the positive input channel voltage.
If the ADC samples only one input channel, when using differential mode, set the current input channel as the positive input channel and GND as the negative input channel, so that the voltage difference between the two is equal to the voltage of the positive input channel.
Differential mode is used for low voltage detection in the SDK, and the function interface is as follows:
adc_set_diff_input(pin >> 12, GND);
(3) Different ADC Tasks need Switching
Low voltage detection as a most basic ADC sampling uses the Misc channel. In addition to low voltage detection, users will also need to use the Misc channel if they require other ADC tasks. Low voltage detection cannot run simultaneously with other ADC tasks and must be implemented by switching.
Use of Low Battery Detection
In the SDK project example, the proj_ultra_ll_mic and proj_ultra_ll_mic_240 projects both implement the low battery detection function, the user needs to enable the low battery detection function in app_ui.h for use.
(1) Low Battery Detection Initialization
The default initialization in the SDK is:
bat_check_init(ADC_GPIO_PB3, 0, 1, 2200, 2300);
The first parameter pin: set the pin for low battery detection, SDK default PB3.
The second parameter res_up and the third parameter res_down: the voltage division coefficient of battery detection, because the low battery detection is based on GPIO, when the detection value exceeds 3.5V, the value will start to deviate. Therefore, when the detection voltage exceeds 3.5V, the user needs to configure the voltage division coefficient by himself, SDK default no voltage dividing, that is, the res_up value is 0, and the res_down value is 1. For the specific calculation position is in the bat_get_voltage_mvJ interface of the bat.c file.
int bat_get_voltage_mv(void)
{
......
adc_buf[adc_buf_index++] = vol *
(bat_check_info.res_up + bat_check_info.res_down) / bat_check_info.res_down;
......
}
The fourth parameter bat_low_mv: secure voltage, SDK default 2200mV.
The fifth parameter bat_nor_mv: normal voltage, SDK default 2300mV, where the transition phase is between bat_low_mv and bat_nor_mv to prevent frequent switching between low power and normal state.
In addition, the bat_check_info variable is a low battery detection structure.
typedef struct {
adc_input_pin_def_e pin;
int res_up;
int res_down;
int bat_low_mv;
int bat_nor_mv;
u32 tick_debounce;
u32 debounce_time;
u32 tick_check;
u32 check_interval;
int bat_state_curr;
int bat_state_last;
int bat_low;
int bat_vol_mv;
} bat_check_info_s;
The first five variables are input parameters for low battery detection initialization.
The sixth variable tick_debounce: low battery state switching timer, the default value is 1000 in microseconds, which is used to periodically switch the low battery state.
The seventh variable debounce_time: low battery state switching timing value, the default is 1000, unit microsecond.
The eighth variable tick_check: bat_check_task task execution timer, which is used to periodically execute the low battery detection task.
The ninth variable check_interval: bat_check_task task execution timing value, the default is 1000, unit microsecond.
The tenth variable bat_state_curr: the current low battery state.
The eleventh variable bat_state_last: the last low battery state. Judging with the current low battery state, if the current low battery state is inconsistent with the last low battery state, the update operation of the low battery state variable is started.
The twelfth variable bat_low: low battery state variable, 0 is non low battery state, 1 is low battery state, and the user can determine whether it is a low battery state by obtaining the variable state.
The thirteenth variable bat_vol_mv: current battery, unit mV.
(2) Low Battery Detection Processing
In ui_loop, call bat_check_task() to realize low battery detection processing.
The frequency of the low battery detection task is controlled by the variable tick_check. In the project example, the low battery detection is performed every 20ms, developers can modify this time value according to their own needs. When the working voltage changes from higher than 2300mV to lower than 2200mV, the bat_state_last value is 0 and bat_state_curr becomes 1. The current battery is inconsistent with the last battery, the low battery state variable update is started, and the value 1 of bat_state_curr is assigned to bat_low, thereby realizing the switching of the low battery state.
USB Interface
USB Audio Related Macro Description
APP_MODE: to set the USB function, the following three settings are supported:
(1) APP_MODE_DEBUG_ONLY: support USB Log, VCD, command input;
(2) APP_MODE_DEBUG_HID: support USB Log, VCD, command input, HID communication;
(3) APP_MODE_AUDIO_AUTO: support USB Speaker, USB MIC, HID communication.
When APP_MODE == APP_MODE_AUDIO_AUTO, enable the USB Audio function, the following macros can be set:
a) USB_SPEAKER_ENABLE enable USB Speaker (USB downstream);
b) USB_MIC_ENABLE enable USB MIC (USB upstream);
c) USB_MIC_CHANNEL: 1: USB MIC/upstream Mono; 2: USB MIC/upstream Stereo.
Among them, USB Speaker and USB MIC can be enabled independently or simultaneously; USB Speaker only supports stereo.
USB Audio Descriptor Name Initialization
The USB Audio descriptor name initialization is configured in the app_config.h file:
......
#define STRING_PRODUCT L"Low Latency MIC"
#else
#define STRING_PRODUCT L"USB Speacker"
#endif
......
The default value is in the default_config.h file, and it is recommended to modify it in app_config.h.
......
#ifndef STRING_VENDOR
#define STRING_VENDOR L"Telink"
#endif
#ifndef STRING_PRODUCT
#define STRING_PRODUCT L"2.4G Wireless Audio"
#endif
#if (MCU_CORE_TYPE == MCU_CORE_9518)
#ifndef STRING_SERIAL
#define STRING_SERIAL L"TLSR9518"
#endif
#else
#ifndef STRING_SERIAL
#define STRING_SERIAL L"TLSR9518"
#endif
#endif
......
Algorithm
The SDK supports several algorithms.
The audio stream on the transmitter side is as follows:
The audio stream on the receiver side is as follows:
Resampling Algorithm
(1) Resampling Algorithm Initialization
Calling "my_resample48to16_init" and "my_resample16to48_init" in the app_audio_init interface of the app_audio.c file corresponds to downsampling (48k-16k) and upsampling (16k-48k) initialization, respectively.
void app_audio_init (void)
{
......
#if RESAMPLE_48_TO_16_EN
my_resample48to16_init();
#endif
#if RESAMPLE_16_TO_48_EN
my_resample16to48_init();
#endif
.....
}
(2) Resampling Algorithm Processing
The current resampling algorithm processing is mainly suitable for the sampling rate adjustment problem in the noise canceling algorithm processing projects.
(a) In the default noise canceling mode
The Tx side is downsampled before noise canceling and upsampled after noise canceling.
The Rx side is not resampled.
(b) In long distance noise canceling mode
The Tx side downsamples before noise canceling and then encodes.
The Rx side decodes and then upsamples.
EQ
Enable the APP_EQ_ENABLE macro to enable EQ.
(1) EQ Initialization
#if(APP_BUILD == APP_MIC_TX) && APP_EQ_ENABLE
eq_inf_load(0x20000000 + FLASH_USER_EQ_BASE);
myudb_register_hci_eq_cb(my_debug_eq);
audio_codec_flag_set(CODEC_FLAG_EQ_VOICE_MIC_EN, 1);
#endif
eq_inf_load(0x20000000 + FLASH_USER_EQ_BASE): used to read eq data, if the address does not store eq data, use the default eq parameters in the eq.c file:
float32_t coeff_eq_voice_mic_left[5 * NSTAGE_EQ_VOICE_MIC_MAX]={
0.983146317605274,-1.896277815330497,0.925516368712182,1.896277815330497,-0.908662686317456,//800Hz
1.018304336102112,-1.823726756975888,0.907922907437080,1.823726756975888,-0.926227243539192,//2300Hz
1.382406919700922,-0.529089646178078,0.000170953890723,0.529089646178078,-0.382577873591645,//3000Hz
1.018304336102112,-1.823726756975888,0.907922907437080,1.823726756975888,-0.926227243539192,//2300Hz
};
myudb_register_hci_eq_cb(my_debug_eq): used to register the EQ online debugging interface.
audio_codec_flag_set(CODEC_FLAG_EQ_VOICE_MIC_EN, 1): used to enable the EQ of the codec.
(2) EQ Processing
Performe EQ processing in the app_audio_task interface:
_attribute_ram_code_ void app_audio_task ()
{
......
#if APP_EQ_ENABLE
if (audio_codec_flag_get(CODEC_FLAG_EQ_VOICE_MIC_EN))
{
g_eq_para.eq_type = EQ_TYPE_VOICE_MIC;
g_eq_para.eq_sample_rate = EQ_SAMPLE_RATE_48K;
g_eq_para.eq_channel = EQ_CHANNEL_LEFT;
g_eq_para.eq_nstage = instance_eq_voice_mic_left.nstage;
eq_proc(g_eq_para, pcm, pcm, samples, 0);
}
#endif
......
}
The EQ processing is performed by executing the eq_proc function.
(3) EQ Tool
EQ Tool is a tool used by Telink to debug EQ, the following introduces the EQ tool:
First open the Telink BDT tool, click "Tool", and in the drop-down menu, select "TWS Tool".
Select the "EQ Tool", which is divided into 5 main areas, namely:
(a) Visualization area: used to visualize EQ parameters for easy debugging.
(b) Configuration area: used to configure EQ initialization. Among them,
a) FreqSmp: sampling rate, SDK uses 48k by default;
b) Channel: supports left, right and stereo, the SDK uses the left channel by default;
c) Stages: the number of frequency points, 4 frequency points are supported by default;
d) Mode: EQ mode, SDK uses Speech Mic by default;
e) Gain: decrease the gain of all frequency points, the range is -10 ~ 0.
(c) Debug area: used to debug EQ parameters, among them,
a) Type: filter type;
b) Q: slope;
c) Fc(Hz): frequency point;
d) dB: gain.
(d) Operation area: used to update EQ parameters online, save EQ parameter bin files, and generate EQ parameter codes.
(e) Parameter generation area: used to display log and generate EQ parameters.
Next, show how to update the EQ parameters:
(a) USB online update: The APP_MODE macro is configured as APP_MODE_DEBUG_ONLY or APP_MODE_DEBUG_HID, to ensure that the device and PC can print the log, select "USB" in the operation area, click "Download", you can see the parameter update log in the risc-v TDB tool.
(b) Generate parameter update: Click "Get Paramter", the corresponding EQ parameter code will be generated in the generation parameter area, and replace the default eq parameter code mentioned above with this code.
(c) Generate file update: Click "Save as bin file", generate the bin file of eq in the specified path, and then use the Download Tool of Telink TDB to burn the bin file to the address of 0xDC000.
Noise Canceling Algorithm
Enable the APP_NS_ENABLE macro to enable the noise canceling function.
Enable the APP_WB_NS_ENABLE macro, webrtc algorithm is used (better effect), otherwise speex algorithm is used.
The APP_NS_MODE macro selects the noise canceling mode:
FS_16K_NS_EN, default noise canceling mode, processing - Tx side resample 1frame 48K data to 16K -> NS -> resample to 48K again, encode, send N times, Rx receive packet N times, decode, play.
LD_16K_NS_EN, long distance noise canceling mode, processing - Tx side resample 3frame 48K data to 16K -> NS encode, send 3N times, Rx receive packet 3N times, decode, resample to 48K, play.
Note:
Long distance noise reduction mode only supports LC3+ coding, SBC coding is not supported.
(1) Noise Canceling Algorithm Initialization
Initialization in the void app_audio_init() interface.
void app_audio_init ()
{
......
#if APP_WB_NS_ENABLE
app_w_ns_init();
#else
app_ns_init();
#endif
......
}
(a) speex algorithm
void app_ns_init(void)
{
nsParas.noise_suppress_default = -15;
nsParas.echo_suppress_default = -55;
nsParas.echo_suppress_active_default = -45;
nsParas.low_shelf_enable = 1;
nsParas.ns_smoothness = 27853; // QCONST16(0.85f,15)
nsParas.ns_threshold_low = 100000.0f;
#if 0
int nsSize = ns_get_size();
my_dump_str_data(1, "NS Buffer Size", &nsSize, 4); // 0x234E
den = (SpeexPreprocessState*)malloc(nsSize);
#else
static u32 ns_buffer[0x234E/4+256];
den = (SpeexPreprocessState*)ns_buffer;
#endif
ns_init(den, &nsParas, 16000 * MIC_SAMPLES_PER_PACKET / MIC_SAMPLING_RATE, SPEEX_SAMPLERATE);
}
noise_suppress_default: adjust the noise attenuation amplitude, the unit is db, -15 means attenuation 15db.
low_shelf_enable: the filter for low frequency attenuation. After enabling, there will be obvious attenuation below 100Hz, and the overall signal energy will also be reduced.
Note:
It is recommended not to modify other parameters.
Among them, the input parameter frame_size of the ns_init interface supports a maximum of 120.
(b) webrtc algorithm
void app_w_ns_init(void)
{
w_ns_cfg_param.frame_size = W_NS_FRAME_SIZE;
w_ns_cfg_param.sampleRate = 16000;
w_ns_cfg_param.target_level = k12dB;
w_ns_cfg_param.lowShelf_En = 0;
//int s = w_ns_get_size();
//my_dump_str_data(1, "buffer size: %d\n", &s,4);
w_ns_init(ns_buffer_st, w_ns_cfg_param);
}
Where the input parameter W_NS_FRAME_SIZEframe_si of the app_w_ns_initns_in interface supports a maximum of 80, and packet grouping is performed if the frame length exceeds 80.
(2) Noise Canceling Algorithm Processing
In addition to the differences between the webrtc algorithm and the speex algorithm, the most important thing to pay attention to in the noise canceling process is the difference between the noise canceling modes.
(a) Default noise canceling mode
Tx end processing:
_attribute_ram_code_ void app_audio_task (void)
{
......
#if APP_NS_MODE == FS_16K_NS_EN
if (async.aec_enable) {
s16 pcm16k[samples/3];
my_resample48to16_data((int *)pcm, samples, (int *)pcm16k, 0);
log_task (SL_AUDIO_EN, SL01_task_tws_arbiter_en, 1);
app_ns_process_frame(pcm16k, samples/3);
log_task (SL_AUDIO_EN, SL01_task_tws_arbiter_en, 0);
my_resample16to48_data((int *)pcm16k, samples/3, (int *)pcm, 0);
}
......
ps->cmd = ASYNC_CMD_AUDIO_TAG;
#if CODEC_ALGORITHM_SEL == CODEC_ALGORITHM_SBC
mic_audio_encode(pcm, samples, enc);
tmemcpy (ps->data, enc, ASYNC_AUDIO_DATA_LEN);
#elif CODEC_ALGORITHM_SEL == CODEC_ALGORITHM_LC3A
__UNUSED int leni = my_llenc (0, pcm, samples, enc);
tmemcpy (ps->data, enc, ASYNC_AUDIO_DATA_LEN);
#endif
#endif
......
}
Line 7: Resampling 1frame 48K data to 16K.
Line 9: NS
Line 11: resample to 48K again.
Line 14 ~ 21: SBC/LC3+ encoding, send N times.
Processing on Rx side: receive packet N times, decode, play.
(b) Long distance noise reduction mode
Tx end processing:
_attribute_ram_code_ void app_audio_task (void)
{
......
#elif APP_NS_MODE == LD_16K_NS_EN
if (async.aec_enable) {
app_resample48to16_data_mono(ld_48k_pcm[mic_16k_cnt], samples, ld_16k_pcm[mic_16k_cnt]);
app_ns_process_frame(ld_16k_pcm[mic_16k_cnt], samples/3);
app_resample16to48_data_mono(ld_16k_pcm[mic_16k_cnt], samples/3, pcm); //Downsampling here is used for local loopback output, independent of the wireless audio link.
}
#endif
......
#if APP_NS_ENABLE && (APP_NS_MODE == LD_16K_NS_EN)
if (async.aec_enable) {
if (mic_16k_cnt == 2){
ps->cmd = ASYNC_CMD_AUDIO_TAG_NS;
#if CODEC_ALGORITHM_SEL == CODEC_ALGORITHM_SBC
mic_audio_encode((s16 *)ld_16k_pcm, samples, enc);
tmemcpy (ps->data, enc, ASYNC_AUDIO_DATA_LEN);
#elif CODEC_ALGORITHM_SEL == CODEC_ALGORITHM_LC3A
__UNUSED int leni = my_llenc (0, (int16_t *)ld_16k_pcm, samples, enc);
tmemcpy (ps->data, enc, ASYNC_AUDIO_DATA_LEN);
#endif
}
}
#endif
......
}
Line 6: Resample the 1st / 2nd / 3rd frame 48K data to 16K.
Line 7: NS
Line 12 ~ 25: When mic_16k_cnt is 2, encode 3frame data in LC3+ and send 3N times.
Rx end processing:
_attribute_ram_code_ void app_audio_task (void)
{
......
#if (APP_NS_MODE == LD_16K_NS_EN)
......
if (mic_16k_cnt == 0) {
......
my_lldec (mic_idx, ps, ASYNC_AUDIO_DATA_LEN, ld_16k_dec_pcm[mic_idx], 0);
app_resample16to48_data_mono(mic_idx, ld_16k_dec_pcm[mic_idx] + 0 * (MIC_SAMPLES_PER_PACKET / 3), MIC_SAMPLES_PER_PACKET / 3, pcm_dec_mic[mic_idx]);
}
else if (mic_16k_cnt == 1)
{
app_resample16to48_data_mono(mic_idx, ld_16k_dec_pcm[mic_idx] + 1 * (MIC_SAMPLES_PER_PACKET / 3), MIC_SAMPLES_PER_PACKET / 3, pcm_dec_mic[mic_idx]);
}
else if (mic_16k_cnt == 2)
{
app_resample16to48_data_mono(mic_idx, ld_16k_dec_pcm[mic_idx] + 2 * (MIC_SAMPLES_PER_PACKET / 3), MIC_SAMPLES_PER_PACKET / 3, pcm_dec_mic[mic_idx]);
}
......
}
Line 6 ~ 10: when mic_16k_cnt is 0, the received 3-frame data will be decoded first, and then the 1st frame 16K data will be resampled to 48K.
Line 11 ~ 14: when mic_16k_cnt is 1, the 2nd frame 16K data is resampled to 48K.
Line 15 ~ 18: when mic_16k_cnt is 2, resample the 3rd frame 16K data to 48K.
DRC Algorithm
Dynamic Range Control (DRC) provides compression and amplification capabilities that can make sounds sound softer or louder, a form of signal amplitude adjustment. DRC is widely used in the field of audio signal processing, such as the most common Wide Dynamic Range Compression (WDRC) method in hearing aids, the most commonly used Automatic Gain Control (AGC) method in audio signal processing, etc. Dynamic range control is to map the dynamic range of the input audio signal to the specified dynamic range, usually the dynamic range after mapping is smaller than the dynamic range before mapping, so it is called dynamic range compression. The audio signal can perform overall dynamic range control, or can be divided into several sub-bands to perform dynamic range control separately. For specific principles, please refer to: DRC principle
The DRC in SDK is divided into ADC DRC and DAC DRC controlled by ADC_DRC_EN macro and DAC_DRC_EN macro respectively.
The following is an example of ADC DRC.
(1) DRC Initialization
Enable the ADC_DRC_EN macro to enable the DRC algorithm.
void app_drc_init(drc_nodes_s *drc_nodes, u32 flash_addr)
{
app_flash_read(flash_addr, sizeof(drc_paras_set_num), (u8 *)&drc_paras_set_num);
if (drc_paras_set_num > 0 && drc_paras_set_num <= DRC_NODE_MAX) {
u32 addr = FLASH_DRC_PARA_BASE + sizeof(drc_paras_set_num);
app_flash_read(addr, drc_paras_set_num * sizeof(drc_node_para_s), (u8 *)drc_paras_set);
drc_nodes_init_paras(drc_nodes, drc_paras_set, drc_paras_set_num);
drc_paras_set_num = 0;
}
else{
#if DAC_DRC_EN
drc_nodes_init_paras(drc_nodes, dac_drc_node_paras_default, ARRAY_LEN(dac_drc_node_paras_default));
#elif ADC_DRC_EN
drc_nodes_init_paras(drc_nodes, adc_drc_node_paras_default, ARRAY_LEN(adc_drc_node_paras_default));
#endif
}
drc_nodes_update(drc_nodes);
}
The parameters of the initialization DRC algorithm are first read from the address where the Flash is located. If the DRC parameters are not stored to the specified address, the default DRC parameters are used for initialization.
drc_node_para_s adc_drc_node_paras_default[] =
{
{
.type = DRC_NOISE_GATE,
.noise_gate = {
-50.0f, 4.0f, 0.05f, 0.1f, 0.05f, MIC_SAMPLING_RATE, VALUE_0dBFS, 1
},
},
{
.type = DRC_EXPANDER,
.expander = {
-70.0f, (float)20, (float)4, 0.01f, 0.05f, 0.000f, (float)MIC_SAMPLING_RATE, 0, VALUE_0dBFS, 48*2
},
},
{
.type = DRC_COMPRESSOR,
.compressor = {
-6.0f, 3.0f, 4.0f, 0.05f, 1.0f, MIC_SAMPLING_RATE, 0, VALUE_0dBFS, 48
},
},
{
.type = DRC_LIMITER,
.limiter = {
-3.0f, 1.0f, 0.01f, 0.1f, MIC_SAMPLING_RATE, 0, VALUE_0dBFS, 48
},
},
};
(2) DRC Processing
Call the drc_process_frame interface for DRC processing.
static inline PCM_MONO_INT drc_gain_process_single_point(PCM_MONO_INT pcm, float gain)
{
float v = (float)pcm * gain;
for(int j=0; j<__drc_nodes->drc_cnt; j++) {
v = drc_calc_output(&__drc_nodes->drc[j], v);
}
v += 0.5f;
return SATURATE_PCM(v);
}
_attribute_ram_code_ int drc_gain_process_frame(drc_nodes_s *drc_nodes, PCM_MONO_INT *pcm, int len, float gain)
{
__drc_nodes = drc_nodes;
for(int i=0;i<len;i++) {
pcm[i] = drc_gain_process_single_point(pcm[i], gain);
}
return len;
}
(3) DRC Tool
The DRC tool is used to debug DRC and generate corresponding parameter codes. The interface is as follows:
(s) Noise gate: a type of expander that limits signals below a given threshold.
a) Threshold: close the gate dB;
b) Attack: time from closing to opening;
c) Release: time from opening to closing;
d) Hold: holding time of gate opening state;
e) Samples: calculated points, the default is calculating once every 96 points.
(b) Dynamic range expander: attenuates the volume of small sound signals below a given threshold, can make small sound signals sound even quieter.
a) Threshold: extended dB;
b) Width: inflection point excessive smoothing degree, used for smoothing;
c) Ratio: slope;
d) Makeup: gain;
e) Others are the same as above.
(c) Dynamic range compressor: attenuates the volume of loud signals above a given threshold, protecting the hardware and increasing the overall loudness.
(d) Dynamic range limiter: a type of compressor that can limit signals that exceed a given threshold.
After setting the DRC parameters, click "Get Code".
Then the DRC parameter code will be generated, as shown in the following figure:
Replacing the default DRC parameter variable with this parameter will implement the import of DRC parameters.
Or enable CFG_COMMUNICATION_BY_USB macro and configure APP_MODE to APP_MODE_DEBUG_HID or APP_MODE_AUDIO_AUTO to enable online debugging. (APP_MODE_DEBUG_HID mode is used here to view the log)
When it prints USB Connected, it means the connection is successful.
Then check the required parameters and click Download to download the parameters.
Finally, you can view the DRC online debugging parameters sent by the host computer in the log of the console computer.
Note:
DRC online debugging will perform parameter comparison, if the parameters sent are the same as the currently adopted parameters, they will not be processed.
Audio
Audio Initialization
(1) TX Audio Initialization
Audio input sources on the Tx end can be I2S, lineIn, DMIC and AMIC. Supports local Lineout to monitor input audio, and can be turned off, controlled by the CODEC_DAC_MONO_MODE macro.
(a) LineIn/AMIC: the default audio input path of the development board, Linein is the jumper cap short connection PIN 34/33.
If you need to switch to the analog microphone path, adjust PIN 34/33 jumper cap to PIN 30/29 (Left_Mic is used by default).
(b) DMIC: To directly use the peripheral audio processing chip to read the digital signal to R9, and set the "AUDIO_IN_MODE" macro to "DMIC_INPUT".
void app_audio_init ()
{
......
#ifdef AUDIO_IN_MODE
if (AUDIO_IN_MODE == DMIC_INPUT) {
#ifdef DMIC_GPIO_SEL
audio_set_dmic_input(DMIC_GPIO_SEL);
#endif
}
else {
audio_set_input_mode(AUDIO_IN_MODE);
}
#endif
......
}
(c) I2S: the default is the host, and the "AUDIO_INTERFACE" macro needs to be set to "AUDIO_I2S_GPIO".
void app_audio_init ()
{
......
#if(AUDIO_INTERFACE == AUDIO_I2S_GPIO)
audio_set_interface(FLAG_INTERFACE_GPIO, 1);
#if (AUDIO_INTERFACE_ROLE == AUDIO_I2S_AS_SLAVE)
gpio_input_en(I2S_BCK_PC3|I2S_DAC_LR_PC6|I2S_DAC_DAT_PC7);
audio_set_interface(FLAG_INTERFACE_ROLE_SLAVE, 1);
#else
audio_set_interface(FLAG_INTERFACE_ROLE_SLAVE, 0);
#endif
#else
......
}
The I2S initialization pins are: PC3 (BCK), PC6 (LR), PC7 (DAT_IN).
(2) RX Audio Initialization
(a) Lineout: default audio output. If USB Audio is enabled then USB MIC upstream is added, and at the same time, the USB Audio downstream and the remote Tx audio by the Lineout output will be supported for mixing processing.
(b) I2S: audio output for optional switching, set the "AUDIO_INTERFACE" macro to "AUDIO_I2S_GPIO", and the initialization pins are: PC3(BCK), PC4(LR), PC5(DAT_OUT). If USB Audio is enabled then USB MIC upstream is added, and at the same time, the USB Audio downstream and the remote Tx audio by the I2S output will be supported for mixing processing.
(3) Gain Initialization
(a) Tx end: the input gain of AMIC, DMIC and Lineout gain can be adjusted, which are controlled by CODEC_IN_A_GAIN(0 ~ 20dB), CODEC_IN_D_GAIN(0 ~ 43dB), CODEC_OUT_A_GAIN(-19 ~ 12dB), CODEC_OUT_D_GAIN(-31 ~ 32dB) respectively.
(b) Rx end: only Lineout gain is valid, controlled by CODEC_OUT_A_GAIN and CODEC_OUT_D_GAIN, the initialization interface is the same as above.
void app_audio_init ()
{
audio_set_codec_in_path_a_d_gain(CODEC_IN_D_GAIN, CODEC_IN_A_GAIN); //mic sco
audio_set_codec_out_path_a_d_gain(CODEC_OUT_D_GAIN, CODEC_OUT_A_GAIN); //mic sco
.......
}
Configure the Tx / Rx gain via app_ui.h file.
#if APP_BUILD == APP_MIC_TX
#define CODEC_IN_A_GAIN CODEC_IN_A_GAIN_0_DB
#define CODEC_IN_D_GAIN CODEC_IN_D_GAIN_0_DB
#define CODEC_OUT_A_GAIN CODEC_OUT_A_GAIN_0_DB
#define CODEC_OUT_D_GAIN CODEC_OUT_D_GAIN_0_DB
#elif APP_BUILD == APP_MIC_RX
#define CODEC_IN_A_GAIN CODEC_IN_A_GAIN_0_DB
#define CODEC_IN_D_GAIN CODEC_IN_D_GAIN_0_DB
#define CODEC_OUT_A_GAIN CODEC_OUT_A_GAIN_0_DB
#define CODEC_OUT_D_GAIN CODEC_OUT_D_GAIN_0_DB
#endif
Encoding
The SBC, LC3+ codec algorithms are provided in the SDK, and different needs lead to different encoder configurations, mainly through the following key macros:
(1) MIC_SAMPLING_RATE: used to configure the microphone sample rate, the default is 48K..
(2) CODEC_ALGORITHM_SEL: used to configure the encoding algorithm, optional SBC and LC3+.
(3) MIC_SAMPLES_PER_PACKET: the number of samples per frame of the microphone, the corresponding values 鈥嬧€媜f different modes and different sampling rates are also different, as follows:
a) SBC code: 96, 120, 240.
b) LC3+: 120, 240.
(4) SBC_BIT_POOL, SBC_BLOCK_NUM: SBC encoder configuration item, there will be different configurations according to MIC_SAMPLES_PER_PACKET:
a) 96 Frame: 20 | 12;
b) 120 Frame: 26 | 15; c) 240 Frame: 19 | 30.
(5) LC3A_BIT_RATE: LC3 encoder configuration item, unit bit rate, there will be different configurations according to MIC_SAMPLES_PER_PACKET, among which:
a) 120 Frame: 96000;
b) 240 Frame: 72000.
(6) ASYNC_AUDIO_DATA_LEN: encoded packet length, in bytes.
(7) MIC_PACKET_PER_TX: the number of times a single Tx is sent.
a) In 1-to-1 mode, MIC_PACKET_PER_TX is the number of MIC_PACKET_NUM Tx transmissions.
b) In 2-to-1 mode, MIC_PACKET_PER_TX = MIC_PACKET_NUM / 2.
(8) MIC_PACKET_IN_TICK: packet sending time, in microseconds, which will affect the final delay.
Users can configure the configuration that meets the project requirements through the orderly combination of the above macros.
The following configuration is related to 48K@16bits encoding:
Audio Data Processing
Depending on the pairing mode, encoding, and sampling, Audio's data processing flow, latency, number of packets sent, and transmission distance will vary.
Algorithm | Frame | Compression ratio | Sampling time(ms) | Transmission times | Latency(ms) |
---|---|---|---|---|---|
SBC | 96 | 17.71% | 2.0 | 3 | 7.980 |
SBC | 120 | 22.08% | 2.5 | 4 | 8.950 |
SBC | 240 | 15.83% | 5.0 | 5 | 13.700 |
LC3+ | 120 | 12.50% | 2.5 | 4 | 10.713 |
LC3+ | 240 | 9.37% | 5.0 | 7 | 15.525 |
Configuration 1: The sampling rate is 48k*16bit, each sampling is 2.0ms, producing 96 samples of a total of 192 bytes of raw PCM data. Perform SBC encoding on these 192 bytes, where BIT_POOL: 20, BLOCK_NUM: 12, and get: ((20*12) + 7)/8 + 4 = 34 bytes of data.
Configuration 2: The sampling rate is 48k*16bit, each sampling is 2.5ms, producing 120 samples of a total of 240 bytes of raw PCM data. Perform SBC encoding on these 240 bytes, where BIT_POOL: 26, BLOCK_NUM: 15, and get: ((26*15) + 7)/8 + 4 = 53 bytes of data.
Configuration 3: The sampling rate is 48k*16bit, each sampling is 5.0ms, producing 240 samples of a total of 480 bytes of raw PCM data. Perform SBC encoding on these 480 bytes, where BIT_POOL: 19, BLOCK_NUM: 30, and get: ((19*30) + 7)/8 + 4 = 76 bytes of data.
Configuration 4: The sampling rate is 48k*16bit, each sampling is 2.5ms, producing 120 samples of a total of 240 bytes of raw PCM data. Perform LC3+ encoding on these 240 bytes, BIT_RATE: 96000, and get: (96000*(120/120) + 3199)/3200 = 30 bytes of data.
The project example proj_ultra_ll_mic_24bits: sampling rate is 48k*24bit, 2.5ms per sample, producing 120 samples totaling 360 bytes of raw PCM data. LC3+ encode this 360 bytes, BIT_RATE:96000, still 30 bytes data due to algorithm support.
Configuration 5: The sampling rate is 48k*16bit, each sampling is 5.0ms, producing 240 samples of a total of 480 bytes of raw PCM data. Perform LC3+ encoding on these 480 bytes, BIT_RATE: 72000, and get: (72000*(240/120) + 3199)/3200 = 45 bytes of data.
The project example proj_ultra_ll_mic_24bits: sampling rate is 48k*24bit, 5.0ms per sample, generating 240 samples totaling 720 bytes of raw PCM data. LC3+ encoding of these 720 bytes, BIT_RATE:72000, still 45 bytes of data due to algorithm support.