安森美RSL10开发
最近开发RSL10的芯片,目前测试下,该平台在低功耗表现出色,双核ARM+DSP,在测试主频6MHz下功耗仅为3mA
资料下载
开发环境配置
以IAR8.32为例,对于IDE的界面就不截图了。
同时官方提供,初始工程配置的引导教程:RSL10_getting_started_guide.pdf
SDK加入
工程目录分配如下:官方的SDK直接拷贝进来,其他工程文件自己组织。
添加头文件目录
$TOOLKIT_DIR$\CMSIS\Driver\Include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\dma_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\gpio_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\printf
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\rtt
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\i2c_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\RTE_config
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\timer_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\sai_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\usart_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\spi_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\drivers\pwm_driver\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\flashlib\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\flashlib\port
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\source\firmware\ble_abstraction_layer\ble\include
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\include\kernel
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\include\bb
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\include\ble
$PROJ_DIR$\ONSemiconductor\RSL10\3.5.285\include\ble\profiles
添加宏定义
RSL10_CID=101
_RTE_
CFG_BLE=1
CFG_ALLROLES=1
CFG_CON=8
CFG_APP
CFG_APP_BATT
CFG_ATTC=1
CFG_EMB=1
CFG_HOST=1
CFG_RF_ATLAS=1
CFG_ALLPRF=1
CFG_PRF=1
CFG_NB_PRF=2
CFG_CHNL_ASSESS=1
CFG_SEC_CON=1
CFG_EXT_DB
CFG_PRF_BASC=1
分散加载文件
/* ----------------------------------------------------------------------------
* Copyright (c) 2019 Semiconductor Components Industries, LLC
* (d/b/a ON Semiconductor). All Rights Reserved.
*
* This code is the property of ON Semiconductor and may not be redistributed
* in any form without prior written permission from ON Semiconductor. The
* terms of use and warranty for this code are covered by contractual
* agreements between ON Semiconductor and the licensee.
* ----------------------------------------------------------------------------
* sections.icf
* - Simple sections load file
* ----------------------------------------------------------------------------
* $Revision: 1.1 $
* $Date: 2019/07/18 15:49:48 $
* ------------------------------------------------------------------------- */
/* The memory space denoting the maximum possible amount of addressable memory */
define memory Mem with size = 4G;
/* Memory regions in an address space */
define region ROM = Mem:[from 0x00000 size 4K];
/*define region FLASH = Mem:[from 0x0010C800 size 334K];*/
define region FLASH = Mem:[from 0x00100000 size 380K];
define region PRAM = Mem:[from 0x00200000 size 32K];
define region DRAM = Mem:[from 0x20000000 size 24K];
define region DRAM_DSP = Mem:[from 0x20006000 size 48K];
define region DRAM_BB = Mem:[from 0x20012000 size 16K];
/* Create a stack */
define block CSTACK with size = 4K, alignment = 4 { };
define block HEAP with size = 4K, alignment = 4 { };
/* Handle initialization */
do not initialize { section .noinit, section .systemclock };
initialize by copy {readwrite}; /* Initialize RW sections, exclude zero-initialized sections */
/* Place startup code at the start of flash */
place at start of FLASH {readonly object startup_rsl10.o};
/* Place code and data */
place in FLASH {readonly}; /* Place constants and initializers: .rodata and .data_init */
place at start of DRAM {section .systemclock};
/*
* place in DRAM {readwrite}; Place .data, .bss, and .noinit
*/
place in PRAM {readwrite}; /* Place .data, .bss, and .noinit */
place at end of DRAM {block HEAP, block CSTACK};
place in PRAM {section .PRAM32Kmemory}; /* Place .PRAM32Kmemory */
添加源文件
启动文件
库文件
调试打印工具
使用
#include <printf.h>
PRINTF("RSL10 DEVICE INITIALIZED Version v1.2\r\n");
代码
时钟配置
使用内部RF48Mhz晶振,分频系数为1
关于Systick
附图地址:硬件参考手册Page73
Systick时钟源,由寄存器CLCSOURCE确定
以下代码,初始化时钟到48MHz,也可以初始化到8MHz的主频,使用DIO5作为恢复按键,当程序异常跑飞,无法调试下载时,将DIO5接地即可阻塞代码的执行。此时擦除代码,从而恢复正常状态。
/* DIO number that is used for easy re-flashing (recovery mode) */
#define RECOVERY_DIO 5
/* ----------------------------------------------------------------------------
* Function : void Initialize(void)
* ----------------------------------------------------------------------------
* Description : Initialize the system by disabling interrupts, switching to
* the 48 MHz clock.
* Inputs : None
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
static void Initialize(void)
{
/* Mask all interrupts */
__set_PRIMASK(PRIMASK_DISABLE_INTERRUPTS);
/* Disable all existing interrupts, clearing all pending source */
Sys_NVIC_DisableAllInt();
Sys_NVIC_ClearAllPendingInt();
/* Test DIO12 to pause the program to make it easy to re-flash */
DIO->CFG[RECOVERY_DIO] = DIO_MODE_INPUT | DIO_WEAK_PULL_UP |
DIO_LPF_DISABLE | DIO_6X_DRIVE;
while (DIO_DATA->ALIAS[RECOVERY_DIO] == 0);
/* Prepare the 48 MHz crystal
* Start and configure VDDRF */
ACS_VDDRF_CTRL->ENABLE_ALIAS = VDDRF_ENABLE_BITBAND;
ACS_VDDRF_CTRL->CLAMP_ALIAS = VDDRF_DISABLE_HIZ_BITBAND;
/* Wait until VDDRF supply has powered up */
while (ACS_VDDRF_CTRL->READY_ALIAS != VDDRF_READY_BITBAND);
/* Disable RF TX power amplifier supply voltage and
* connect the switched output to VDDRF regulator */
ACS_VDDPA_CTRL->ENABLE_ALIAS = VDDPA_DISABLE_BITBAND;
ACS_VDDPA_CTRL->VDDPA_SW_CTRL_ALIAS = VDDPA_SW_VDDRF_BITBAND;
/* Enable RF power switches */
SYSCTRL_RF_POWER_CFG->RF_POWER_ALIAS = RF_POWER_ENABLE_BITBAND;
/* Remove RF isolation */
SYSCTRL_RF_ACCESS_CFG->RF_ACCESS_ALIAS = RF_ACCESS_ENABLE_BITBAND;
/* Set radio clock accuracy in ppm */
BLE_DeviceParam_Set_ClockAccuracy(RADIO_CLOCK_ACCURACY);
/* Start the 48 MHz oscillator without changing the other register bits */
RF->XTAL_CTRL = ((RF->XTAL_CTRL & ~XTAL_CTRL_DISABLE_OSCILLATOR) |
XTAL_CTRL_REG_VALUE_SEL_INTERNAL);
#if USE_HI_FQ_16K_AUDIO_SAMPLE
/* Enable 48 MHz oscillator divider at desired prescale value */
RF_REG2F->CK_DIV_1_6_CK_DIV_1_6_BYTE = CK_DIV_1_6_PRESCALE_1_BYTE;
#else
/* Enable 48 MHz oscillator divider at desired prescale value ->8MHz*/
RF_REG2F->CK_DIV_1_6_CK_DIV_1_6_BYTE = CK_DIV_1_6_PRESCALE_6_BYTE;
#endif
/* Wait until 48 MHz oscillator is started */
while (RF_REG39->ANALOG_INFO_CLK_DIG_READY_ALIAS !=
ANALOG_INFO_CLK_DIG_READY_BITBAND);
/* Switch to 48 MHz oscillator clock */
Sys_Clocks_SystemClkConfig(JTCK_PRESCALE_1 |
EXTCLK_PRESCALE_1 |
SYSCLK_CLKSRC_RFCLK);
/* Configure clock dividers */
#if USE_HI_FQ_16K_AUDIO_SAMPLE
CLK->DIV_CFG0 = (SLOWCLK_PRESCALE_48 | BBCLK_PRESCALE_6 |
USRCLK_PRESCALE_2);
CLK->DIV_CFG2 = (CPCLK_PRESCALE_8 | DCCLK_PRESCALE_12);
#else
CLK->DIV_CFG0 = (SLOWCLK_PRESCALE_8 | BBCLK_PRESCALE_1 |
USRCLK_PRESCALE_1);
CLK->DIV_CFG2 = (CPCLK_PRESCALE_8 | DCCLK_PRESCALE_2);
#endif
BBIF->CTRL = (BB_CLK_ENABLE | BBCLK_DIVIDER_8 | BB_WAKEUP);
/* Stop masking interrupts */
__set_PRIMASK(PRIMASK_ENABLE_INTERRUPTS);
__set_FAULTMASK(FAULTMASK_ENABLE_INTERRUPTS);
}
定时器操作
依赖TIMER_RSLxx.c
Systick启动
static uint32_t Timer_Port_TimeMS = 0;
static uint32_t Timer_Port_TimeSec = 0;
/*初始化Systick,设置事件回调*/
Driver_TIMER.Initialize(TIMER_Port_SignalEvent);
/*启动Systick*/
Driver_TIMER.Start(TIMER_SYSTICK);
/**
******************************************************************
* @brief 定时事件回调
* @param [in]None.
* @return None.
* @author aron566
* @version V1.0
* @date 2021-07-20
******************************************************************
*/
static void TIMER_Port_SignalEvent(uint32_t event)
{
if(TIMER_SYSTICK_EVENT & event)
{
Timer_Port_IRQHandler();
}
}
/**
******************************************************************
* @brief 定时器中断回调
* @param [in]None
* @return None.
* @author aron566
* @version V1.0
* @date 2021-01-13
******************************************************************
*/
static inline void Timer_Port_IRQHandler(void)
{
Timer_Port_TimeMS++;
if(Timer_Port_TimeMS == 1000)
{
Timer_Port_TimeMS = 0;
Timer_Port_TimeSec++;
}
}
串口操作
依赖USART_RSLxx.c
,同时需要在RTE_Device.h
定义好串口的RX与TX脚,不要与其他脚冲突。
static uint8_t RX_Buff_Temp[256];
/*初始化,设置事件回调*/
Driver_USART0.Initialize(RSL10_USART_SignalEvent);
/*启动接收*/
Driver_USART0.Receive(RX_Buff_Temp, 1);
/************************************************************
* @brief RSL10串口事件回调
* @param event 事件.
* @return None
* @author aron566
* @date 2020/7/19
* @version v1.0
* @note @@
***********************************************************/
static void RSL10_USART_SignalEvent(uint32_t event)
{
if(event & ARM_USART_EVENT_SEND_COMPLETE)
{
/*发送完成回调*/
}
if(event & ARM_USART_EVENT_RECEIVE_COMPLETE)
{
/*接收到数据*/
}
}
I2C操作
依赖I2C_RSLxx.c
,同时需要在RTE_Device.h
定义好串口的SDA与SCL脚,不要与其他脚冲突。
这里的地址需要按照标准I2C的格式7bit或者10bit方式写入
如上图这样的地址作为本机地址:那么从机地址就是0x06 Slave_Addr = 0x06
/* Initialize i2c, register callback function */
Driver_I2C0.Initialize(I2C_EventCallback);
/*配置I2C自身地址*/
Driver_I2C0.Control(ARM_I2C_OWN_ADDRESS, Slave_Addr);
/* Wait for new transfer as slave */
Driver_I2C0.SlaveReceive(I2C_RX_Buf, SLAVE_RX_SIZE_MIN);
PDM操作
DMA使用注意
在使用DMA音频传输需要注意格式问题即配置问题,DMA最小的step步长32bit,当配置为LSB对齐,数据长度为16bit音频数据,配置如下
方向:外设到内存,内存存储区为DMIC0_Audio_Data,取16bit宽度(这里无需在意DMA最小的step步长32bit,因为他是静态外设地址),DMA需要配置为:从外设取16bit数据宽度,目标存储宽度32bit(为了配合OD的DMA 内存到外设 最小的step步长32bit,所以存储宽度为32bit)
static int16_t DMIC0_Audio_Data[320] = {0};
static int16_t OD_Audio_Data[320] = {0};
/*音频DMA配置*/
#if USE_AUDIO_DMA_MODE
// <h>Source configuration
// ===============================
//
// <o>Source target
// <0x0=> I2C <0x1=> SPI0
// <0x2=> SPI1 <0x3=> PCM
// <0x4=> UART <0x5=> ASRC
// <0x6=> PBUS <0x7=> DMIC
// <0x8=> MEMORY
// <i> Defines the source target
// <i> Default: MEMORY
#ifndef DMIC0_DMA_SRC_SEL_DEFAULT
#define DMIC0_DMA_SRC_SEL_DEFAULT 7
#endif
// <o>Step mode
// <0x0=> Increment
// <0x1=> Decrement
// <0x2=> Static
// <i> Defines if the source address should be incremented / decremented during the trasnfer
// <i> Default: Increment
#ifndef DMIC0_DMA_SRC_STEP_MODE_DEFAULT
#define DMIC0_DMA_SRC_STEP_MODE_DEFAULT 2
#endif
// <o>Step size
// <0x0=> 1 x 32bits
// <0x1=> 2 x 32bits
// <0x2=> 3 x 32bits
// <0x3=> 4 x 32bits
// <i> Defines the source address step size. Valid only if increment / decrement step mode was selected
// <i> Default: 1 x 32bit
#ifndef DMIC0_DMA_SRC_STEP_SIZE_DEFAULT
#define DMIC0_DMA_SRC_STEP_SIZE_DEFAULT 0
#endif
// <o>Word size
// <0x3=> 4bits
// <0x0=> 8bits
// <0x1=> 16bits
// <0x2=> 32bits
// <i> Defines the single source word size
// <i> Default: 8bits
#ifndef DMIC0_DMA_SRC_WORD_SIZE_DEFAULT
#define DMIC0_DMA_SRC_WORD_SIZE_DEFAULT 1/**< 搬运宽度*/
#endif
// </h>
// <h>Destination configuration
// ===============================
//
// <o>Destination target
// <0x0=> I2C <0x1=> SPI0
// <0x2=> SPI1 <0x3=> PCM
// <0x4=> UART <0x5=> ASRC
// <0x6=> PBUS <0x7=> DMIC
// <0x8=> MEMORY
// <i> Defines the destination target
// <i> Default: MEMORY
#ifndef DMIC0_DMA_DST_SEL_DEFAULT
#define DMIC0_DMA_DST_SEL_DEFAULT 8
#endif
// <o>Step mode
// <0x0=> Increment
// <0x1=> Decrement
// <0x2=> Static
// <i> Defines if the destination address should be incremented / decremented during the trasnfer
// <i> Default: Increment
#ifndef DMIC0_DMA_DST_STEP_MODE_DEFAULT
#define DMIC0_DMA_DST_STEP_MODE_DEFAULT 0
#endif
// <o>Step size
// <0x0=> 1 x 32bits
// <0x1=> 2 x 32bits
// <0x2=> 3 x 32bits
// <0x3=> 4 x 32bits
// <i> Defines the destination address step size. Valid only if increment / decrement step mode was selected
// <i> Default: 1 x 32bit
#ifndef DMIC0_DMA_DST_STEP_SIZE_DEFAULT
#define DMIC0_DMA_DST_STEP_SIZE_DEFAULT 0
#endif
// <o>Word size
// <0x3=> 4bits
// <0x0=> 8bits
// <0x1=> 16bits
// <0x2=> 32bits
// <i> Defines the single destination word size
// <i> Default: 8bits
#ifndef DMIC0_DMA_DST_WORD_SIZE_DEFAULT
#define DMIC0_DMA_DST_WORD_SIZE_DEFAULT 2/**< 数据存放格式*/
#endif
// </h>
// <o>Data transfer mode
// <0x0=> Repeat
// <0x1=> Single
// <i> Defines if the transfer should be executed once or repeatedly
// <i> Default: Single
#ifndef DMIC0_DMA_DATA_MODE_DEFAULT
#define DMIC0_DMA_DATA_MODE_DEFAULT 0
#endif
// <o>Data endianness
// <0x0=> Little Endian
// <0x1=> Big endian
// <i> Defines the data bytes order to be used
// <i> Default: Little Endian
#ifndef DMIC0_DMA_BYTE_ORDER_DEFAULT
#define DMIC0_DMA_BYTE_ORDER_DEFAULT 0
#endif
// <o>Channel priority
// <0x0=> 0 <0x1=> 1
// <0x2=> 2 <0x3=> 3
// <i> Defines the channel priority
// <i> Default: 0
#ifndef DMIC0_DMA_CHANNEL_PRIORITY_DEFAULT
#define DMIC0_DMA_CHANNEL_PRIORITY_DEFAULT 0
#endif
// <h>Interrupt priority configuration
// ===============================
//
// <o>Pre-empt priority <0-0x7>
// <i> Defines the pre-empt priority
// <i> Default: 0
#ifndef DMIC0_DMA_INT_PREEMPT_PRI
#define DMIC0_DMA_INT_PREEMPT_PRI 0
#endif
// <o>Subgroup priority <0-0x7>
// <i> Defines the subgroup priority
// <i> Default: 0
#ifndef DMIC0_DMA_INT_SUBGRP_PRI
#define DMIC0_DMA_INT_SUBGRP_PRI 0
#endif
// </h>
// </e>
// </e>
// <h>Source configuration
// ===============================
//
// <o>Source target
// <0x0=> I2C <0x1=> SPI0
// <0x2=> SPI1 <0x3=> PCM
// <0x4=> UART <0x5=> ASRC
// <0x6=> PBUS <0x7=> DMIC
// <0x8=> MEMORY
// <i> Defines the source target
// <i> Default: MEMORY
#ifndef OD_DMA_SRC_SEL_DEFAULT
#define OD_DMA_SRC_SEL_DEFAULT 8
#endif
// <o>Step mode
// <0x0=> Increment
// <0x1=> Decrement
// <0x2=> Static
// <i> Defines if the source address should be incremented / decremented during the trasnfer
// <i> Default: Increment
#ifndef OD_DMA_SRC_STEP_MODE_DEFAULT
#define OD_DMA_SRC_STEP_MODE_DEFAULT 0
#endif
// <o>Step size
// <0x0=> 1 x 32bits
// <0x1=> 2 x 32bits
// <0x2=> 3 x 32bits
// <0x3=> 4 x 32bits
// <i> Defines the source address step size. Valid only if increment / decrement step mode was selected
// <i> Default: 1 x 32bit
#ifndef OD_DMA_SRC_STEP_SIZE_DEFAULT
#define OD_DMA_SRC_STEP_SIZE_DEFAULT 0
#endif
// <o>Word size
// <0x3=> 4bits
// <0x0=> 8bits
// <0x1=> 16bits
// <0x2=> 32bits
// <i> Defines the single source word size
// <i> Default: 8bits
#ifndef OD_DMA_SRC_WORD_SIZE_DEFAULT
#define OD_DMA_SRC_WORD_SIZE_DEFAULT 2/**< 源数据格式固定*/
#endif
// </h>
// <h>Destination configuration
// ===============================
//
// <o>Destination target
// <0x0=> I2C <0x1=> SPI0
// <0x2=> SPI1 <0x3=> PCM
// <0x4=> UART <0x5=> ASRC
// <0x6=> PBUS <0x7=> DMIC
// <0x8=> MEMORY
// <i> Defines the destination target
// <i> Default: MEMORY
#ifndef OD_DMA_DST_SEL_DEFAULT
#define OD_DMA_DST_SEL_DEFAULT 7
#endif
// <o>Step mode
// <0x0=> Increment
// <0x1=> Decrement
// <0x2=> Static
// <i> Defines if the destination address should be incremented / decremented during the trasnfer
// <i> Default: Increment
#ifndef OD_DMA_DST_STEP_MODE_DEFAULT
#define OD_DMA_DST_STEP_MODE_DEFAULT 2
#endif
// <o>Step size
// <0x0=> 1 x 32bits
// <0x1=> 2 x 32bits
// <0x2=> 3 x 32bits
// <0x3=> 4 x 32bits
// <i> Defines the destination address step size. Valid only if increment / decrement step mode was selected
// <i> Default: 1 x 32bit
#ifndef OD_DMA_DST_STEP_SIZE_DEFAULT
#define OD_DMA_DST_STEP_SIZE_DEFAULT 0
#endif
// <o>Word size
// <0x3=> 4bits
// <0x0=> 8bits
// <0x1=> 16bits
// <0x2=> 32bits
// <i> Defines the single destination word size
// <i> Default: 8bits
#ifndef OD_DMA_DST_WORD_SIZE_DEFAULT
#define OD_DMA_DST_WORD_SIZE_DEFAULT 1/**< 每传输宽度*/
#endif
// </h>
// <o>Data transfer mode
// <0x0=> Repeat
// <0x1=> Single
// <i> Defines if the transfer should be executed once or repeatedly
// <i> Default: Single
#ifndef OD_DMA_DATA_MODE_DEFAULT
#define OD_DMA_DATA_MODE_DEFAULT 0
#endif
// <o>Data endianness
// <0x0=> Little Endian
// <0x1=> Big endian
// <i> Defines the data bytes order to be used
// <i> Default: Little Endian
#ifndef OD_DMA_BYTE_ORDER_DEFAULT
#define OD_DMA_BYTE_ORDER_DEFAULT 0
#endif
// <o>Channel priority
// <0x0=> 0 <0x1=> 1
// <0x2=> 2 <0x3=> 3
// <i> Defines the channel priority
// <i> Default: 0
#ifndef OD_DMA_CHANNEL_PRIORITY_DEFAULT
#define OD_DMA_CHANNEL_PRIORITY_DEFAULT 0
#endif
// <h>Interrupt priority configuration
// ===============================
//
// <o>Pre-empt priority <0-0x7>
// <i> Defines the pre-empt priority
// <i> Default: 0
#ifndef OD_DMA_INT_PREEMPT_PRI
#define OD_DMA_INT_PREEMPT_PRI 0
#endif
// <o>Subgroup priority <0-0x7>
// <i> Defines the subgroup priority
// <i> Default: 0
#ifndef OD_DMA_INT_SUBGRP_PRI
#define OD_DMA_INT_SUBGRP_PRI 0
#endif
// </h>
// </e>
// </e>
#endif ///< USE_AUDIO_DMA_MODE
OD操作
Flash操作
RSL10的Flash操作需要注意,操作的字节对齐到32位的整数倍。
因为工程使用FAL分区进行管理Flash,这边直接给出接口代码。
依赖官方文件:drv_flash.c
、rsl10_flash_rom.h、rsl10_sys_flash.c
/**
* @file fal_flash_rsl10h7_port.c
*
* @date 2021-07-31
*
* @author aron566
*
* @copyright aron566.
*
* @brief FLASH FAL操作接口 User main memory
*
* @details BANK1 RANGE:0x00100000 - 0x0015FFFF TOTAL_SIZE:384KB 芯片信息位于rsl10_map.h
*
* write granularities : 32bit
*
* @version V1.0
*/
#include <fal.h>
#include <string.h>
#include <rsl10.h>
#include "drv_flash.h"
#include "rsl10_flash.h"
#include "Timer_Port.h"
/* base address of the flash sectors */
#define BANK_1_ADDR_FLASH_SECTOR_0 ((uint32_t)0x00100000) /* Base address of Sector 0, 2 K bytes*/
#define BANK_1_ADDR_FLASH_SECTOR_1 ((uint32_t)0x00100800) /* Base address of Sector 1, 2 K bytes*/
#define BANK_1_ADDR_FLASH_SECTOR_2 ((uint32_t)0x00101000) /* Base address of Sector 2, 2 K bytes*/
#ifndef FLASH_PAGE_SIZE
#define FLASH_PAGE_SIZE 2*1024U
#endif
#define FLASH_PAGE_0_ADDR BANK_1_ADDR_FLASH_SECTOR_0 /* 物理flash原始起始页地址 */
#define FLASH_START_PAGE_ADDR BANK_1_ADDR_FLASH_SECTOR_0 /* 分区表起始地址 */
#define CURRENT_PART_START_PAGE ((FLASH_START_PAGE_ADDR-FLASH_PAGE_0_ADDR)/FLASH_PAGE_SIZE) /* 当前分区实际物理起始页 */
#define FLASH_PAGE_NUM_MAX (384/2U)
/*拷贝完成标识*/
static volatile uint8_t Sys_Flash_Copy_Complete = 0;
/* ----------------------------------------------------------------------------
* Function : void FLASH_COPY_IRQHandler(void)
* ----------------------------------------------------------------------------
* Description : Set a status flag to indicate the flash copier
* operation is complete.
* Inputs : None
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void FLASH_COPY_IRQHandler(void)
{
Sys_Flash_Copy_Complete = 1;
}
uint8_t fal_port_get_copy_state(void)
{
return Sys_Flash_Copy_Complete;
}
void fal_port_reset_copy_state(void)
{
Sys_Flash_Copy_Complete = 0;
}
/**
* Get the sector of a given address
*
* @param address flash address
*
* @return The sector of a given address
*/
static uint32_t rsl10_get_sector(uint32_t Startaddress ,size_t size ,uint32_t *get_pages)
{
/*记录起始页与结束页*/
uint32_t Startpage = 0,Endpage = 0;
/*搜索完成标记*/
uint8_t start_page_flag = 0,end_page_flag = 0;
/*计算起始地址所在页*/
if(Startaddress >= rsl10_onchip_flash.addr)
{
for(uint32_t i = 0; i < FLASH_PAGE_NUM_MAX; i++)
{
/*计算首地址所在页*/
if(start_page_flag == 0)
{
if((i*FLASH_PAGE_SIZE+FLASH_PAGE_0_ADDR+FLASH_PAGE_SIZE-1) >= Startaddress)
{
log_i("first erase page:%d",i);
Startpage = i;
start_page_flag = 1;
}
}
/*计算尾地址所在页*/
if(end_page_flag == 0)
{
if((i*FLASH_PAGE_SIZE+FLASH_PAGE_0_ADDR+FLASH_PAGE_SIZE-1) >= (Startaddress+size-1))
{
log_i("end erase page:%d",i);
Endpage = i;
end_page_flag = 1;
break;
}
}
}
}
else
{
/*地址非法*/
*get_pages = 0;
return Startpage;
}
/*计算地址范围内的页数*/
*get_pages = Endpage-Startpage+1;
/*get sector num*/
return Startpage;
/*get addr*/
// return Startpage*FLASH_PAGE_SIZE+FLASH_PAGE_0_ADDR;
}
/**
* Get the sector size
*
* @param sector sector
*
* @return sector size
*/
static uint32_t rsl10_get_sector_size(uint32_t pages)
{
return pages*FLASH_PAGE_SIZE; //!< 2K一页
}
/**
* Get the bankx
*
* @param addr
*
* @return bank num
*/
static uint8_t rsl10_get_addr_bank(uint32_t addr)
{
(void)(addr);
return 0;
}
static int init(void)
{
/* Enable the flash copier interrupt */
NVIC_EnableIRQ(FLASH_COPY_IRQn);
return 0;
}
static int read(long offset, uint8_t *buf, size_t size)
{
uint32_t addr = rsl10_onchip_flash.addr + offset;
uint8_t temp_buf[sizeof(int)] = {0};
uint32_t copy_size = size/sizeof(int);
uint32_t Time_Sec = Timer_Port_Get_Current_Time(TIMER_SEC);
fal_port_reset_copy_state();
Sys_Flash_Copy(addr, (uint32_t)buf, copy_size, COPY_TO_32BIT);
//wait ok
while(Sys_Flash_Copy_Complete == 0)
{
if((Timer_Port_Get_Current_Time(TIMER_SEC) - Time_Sec) > 10)
{
return -1;
}
}
uint32_t copy_size_min = copy_size * sizeof(int) >= size?0:size - copy_size * sizeof(int);
if(copy_size_min == 0)
{
return size;
}
Time_Sec = Timer_Port_Get_Current_Time(TIMER_SEC);
fal_port_reset_copy_state();
Sys_Flash_Copy(addr + copy_size * sizeof(int), (uint32_t)temp_buf, 1, COPY_TO_32BIT);
//wait ok
while(Sys_Flash_Copy_Complete == 0)
{
if((Timer_Port_Get_Current_Time(TIMER_SEC) - Time_Sec) > 10)
{
return -1;
}
}
memmove(buf + copy_size * sizeof(int), temp_buf, size - copy_size * 4);
return size;
}
static int write(long offset, const uint8_t *buf, size_t size)
{
uint32_t addr = rsl10_onchip_flash.addr + offset;
const size_t once_write_size = sizeof(int)*2;
uint8_t write_buf[sizeof(int)*2] = {0};
size_t wr_size = 0;
bool ret = 0;
/*once write 64bits*/
Drv_Flash_Unlock();
size_t write_cnt_max = (((size/once_write_size)*once_write_size) < size)?(size/once_write_size)+1:(size/once_write_size);
for(size_t i = 0; i < write_cnt_max; i++)
{
/*检查已写入长度*/
wr_size = (((i+1)*once_write_size) > size)?(size-(i*once_write_size)):once_write_size;
memmove(write_buf, buf+(i*once_write_size), wr_size);
ret = Drv_Flash_Program(addr+(i*once_write_size), (const uint32_t *)write_buf);
if(ret == false)
{
log_i("write error");
return -1;
}
memset(write_buf, 0xFF, once_write_size);
}
Drv_Flash_Lock();
return size;
}
static int erase(long offset, size_t size)
{
uint32_t flash_status = FLASH_ERR_NONE;
size_t erased_size = 0;
uint32_t addr = rsl10_onchip_flash.addr + offset;
uint32_t erasepages = 0;
/* start erase */
Drv_Flash_Unlock();
/*循环擦除页地址*/
uint32_t Sector = rsl10_get_sector(addr ,size ,&erasepages);
log_i("***Total Erasepages %u****", erasepages);
for(uint32_t index = 0; index < erasepages; index++)
{
Sector = Sector>=FLASH_PAGE_NUM_MAX?Sector-FLASH_PAGE_NUM_MAX:Sector;
log_i("erase page:0x%02X,PAGES:%d BANK:%u", Sector, 1, rsl10_get_addr_bank(addr + FLASH_PAGE_SIZE*index));
flash_status = Drv_Flash_Erase(addr + FLASH_PAGE_SIZE*index);
log_i("addr 0x%08X", addr + FLASH_PAGE_SIZE*index);
if(flash_status == false)
{
log_i("erase error");
return -1;
}
Sector++;
}
erased_size += rsl10_get_sector_size(erasepages);
log_i("erase size:%d", erased_size);
Drv_Flash_Lock();
return size;
}
const struct fal_flash_dev rsl10_onchip_flash =
{
.name = "rsl10_onchip",
.addr = FLASH_PAGE_0_ADDR,
.len = FLASH_PAGE_SIZE*FLASH_PAGE_NUM_MAX,
.blk_size = FLASH_PAGE_SIZE,
.ops = {init, read, write, erase},
.write_gran = 32
};
BLE配置
BLE内核由官方封装精简后打包成库文件,所以这部分代码看不到,而且必须使用他的事件库内核
工程需要包含几个BLE库文件
时钟配置
/**
******************************************************************
* @brief 初始化时钟、BLE协议栈、消息处理内核
* @param [in]None.
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/12
******************************************************************
*/
static inline void BLE_Port_Clock_Init(void)
{
/* Configure the current trim settings for VCC, VDDA */
ACS_VCC_CTRL->ICH_TRIM_BYTE = VCC_ICHTRIM_16MA_BYTE;
ACS_VDDA_CP_CTRL->PTRIM_BYTE = VDDA_PTRIM_16MA_BYTE;
/* Disable RF TX power amplifier supply voltage and
* connect the switched output to VDDRF regulator */
ACS_VDDPA_CTRL->ENABLE_ALIAS = VDDPA_DISABLE_BITBAND;
ACS_VDDPA_CTRL->VDDPA_SW_CTRL_ALIAS = VDDPA_SW_VDDRF_BITBAND;
/* Set radio clock accuracy in ppm */
BLE_DeviceParam_Set_ClockAccuracy(RADIO_CLOCK_ACCURACY);
/*8MHz / 8*/
BBIF->CTRL = (BB_CLK_ENABLE | BBCLK_DIVIDER_8 | BB_WAKEUP);
/* Seed the random number generator */
srand(1);
/* Initialize the kernel and Bluetooth stack */
Kernel_Init(0);
BLE_InitNoTL(0);
/* Set radio output power of RF */
Sys_RFFE_SetTXPower(OUTPUT_POWER_DBM);
/* Enable Bluetooth related interrupts */
NVIC->ISER[1] = (NVIC_BLE_CSCNT_INT_ENABLE |
NVIC_BLE_SLP_INT_ENABLE |
NVIC_BLE_RX_INT_ENABLE |
NVIC_BLE_EVENT_INT_ENABLE |
NVIC_BLE_CRYPT_INT_ENABLE |
NVIC_BLE_ERROR_INT_ENABLE |
NVIC_BLE_GROSSTGTIM_INT_ENABLE |
NVIC_BLE_FINETGTIM_INT_ENABLE |
NVIC_BLE_SW_INT_ENABLE);
}
术语
在阅读代码中,熟悉常用的术语简写,有助于理解代码。
简称 | 全名 | 描述 |
---|---|---|
BASS | Battery Service Server | 电池服务 |
DISS / DIS | Device Information Service Server | 设备信息服务 |
Adv | advertising | 广播 |
GAPM | GAP manager | GAP 层 |
BD | Bond | 绑定 |
NB | Number | 数量 |
PRF | Profile | 配置文件 |
STD | standard | 标准 |
SVC | service | 服务 |
IND | indication | 指示 |
conidx | Connection index | 连接号 |
关于消息ID
1、发送消息
/// Builds the task identifier from the type and the index of that task.
#define KE_BUILD_ID(type, index) ( (ke_task_id_t)(((index) << 8)|(type)) )
/* ----------------------------------------------------------------------------
* Function : int GAPC_ConnectionReqInd(ke_msg_idd_t const msg_id,
* struct gapc_connection_req_ind
* const *param,
* ke_task_id_t const dest_id,
* ke_task_id_t const src_id)
* ----------------------------------------------------------------------------
* Description : Handle connection indication message received from GAP
* controller
* Inputs : - msg_id - Kernel message ID number
* - param - Message parameters in format of
* struct gapc_connection_req_ind
* - dest_id - Destination task ID number
* - src_id - Source task ID number
* Outputs : return value - Indicate if the message was consumed;
* compare with KE_MSG_CONSUMED
* Assumptions : None
* ------------------------------------------------------------------------- */
int GAPC_ConnectionReqInd(ke_msg_id_t const msg_id,
struct gapc_connection_req_ind const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
ble_env.conidx = KE_IDX_GET(src_id);
/* Send connection confirmation */
struct gapc_connection_cfm *cfm = KE_MSG_ALLOC(GAPC_CONNECTION_CFM,
KE_BUILD_ID(TASK_GAPC, ble_env.conidx), TASK_APP,
gapc_connection_cfm);
/* Send the message */
ke_msg_send(cfm);
}
在上方的函数中,发送消息,首先申请了一块内存,依据存放不同消息,这块内存大小不一,格式统一:消息ID 、发送的地址:事件ID包含连接号,事件来源,消息体
2、接收消息
注意接收内核的消息在参数src_id会带有conidx信息,而应用层发出的消息src_id为TASK_APP
以GAPC层消息ID为例,可以看到消息ID的枚举类型,是以TASK_FIRST_MSG(TASK_ID_GAPC)
为起始。
rwip_task.h中定义了TASK_ID_GAPC数值为14
/// Build the first message ID of a task. (in fact a ke_msg_id_t)
#define TASK_FIRST_MSG(task) ((uint16_t)((task) << 8))
所以任务的流转:先使用KE_IDX_GET(msg_id) 获取TASK_ID_GAPC 数值知道它是哪一层的消息
/// Retrieves task index number from task id.
#define KE_IDX_GET(ke_task_id) (((ke_task_id) >> 8) & 0xFF)
再前往那一层去处理消息msg_handler.c
/* ----------------------------------------------------------------------------
* Function : int MsgHandler_Notify(ke_msg_id_t const msg_id,
* void const *param, ke_task_id_t const dest_id,
* ke_task_id_t const src_id)
* ----------------------------------------------------------------------------
* Description : Search the lists and call back the functions associated with
* the msg_id, following the priority order (HIGH to LOW). This
* function was designed to be used as the default handler of
* the kernel and shall NOT be called directly by the
* application. To notify an event, the application should
* enqueue a message in the kernel, in order to avoid chaining
* the context of function calls (stack overflow).
* Inputs : msg_id - A task identifier, such as TASK_ID_GAPM
* or a message identifier, such as GAPM_CMP_EVT;
* param - Message parameter
* dest_id - destination task
* src_id - source task
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
int MsgHandler_Notify(ke_msg_id_t const msg_id, void *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
MsgHandler_t *tmp = msgHandlerHead;
uint8_t task_id = KE_IDX_GET(msg_id);
/* First notify abstraction layer handlers */
switch(task_id)
{
case TASK_ID_GAPC:
GAPC_MsgHandler(msg_id, param, dest_id, src_id);
break;
case TASK_ID_GAPM:
GAPM_MsgHandler(msg_id, param, dest_id, src_id);
break;
case TASK_ID_GATTC:
GATTC_MsgHandler(msg_id, param, dest_id, src_id);
break;
case TASK_ID_GATTM:
GATTM_MsgHandler(msg_id, param, dest_id, src_id);
break;
#if RTE_BLE_L2CC_ENABLE
case TASK_ID_L2CC:
L2CC_MsgHandler(msg_id, param, dest_id, src_id);
break;
#endif /* RTE_BLE_L2CC_ENABLE */
}
/* Notify subscribed application/profile handlers */
while(tmp)
{
/* If message ID matches or the handler should be called for all
* messages of this task type */
if((tmp->msg_id == msg_id) || (tmp->msg_id == task_id))
{
tmp->callback(msg_id, param, dest_id, src_id);
}
tmp = tmp->next;
}
return KE_MSG_CONSUMED;
}
/// GAP Controller Task messages
enum gapc_msg_id
{
/* Default event */
/// Command Complete event
GAPC_CMP_EVT = TASK_FIRST_MSG(TASK_ID_GAPC),
/* Connection state information */
/// Indicate that a connection has been established
GAPC_CONNECTION_REQ_IND,
/// Set specific link data configuration.
GAPC_CONNECTION_CFM,
/// Indicate that a link has been disconnected
GAPC_DISCONNECT_IND,
/* Link management command */
/// Request disconnection of current link command.
GAPC_DISCONNECT_CMD,
/* Peer device info */
/// Retrieve information command
GAPC_GET_INFO_CMD,
/// Peer device attribute DB info such as Device Name, Appearance or Slave Preferred Parameters
GAPC_PEER_ATT_INFO_IND,
/// Indication of peer version info
GAPC_PEER_VERSION_IND,
/// Indication of peer features info
GAPC_PEER_FEATURES_IND,
/// Indication of ongoing connection RSSI
GAPC_CON_RSSI_IND,
/* Device Name Management */
/// Peer device request local device info such as name, appearance or slave preferred parameters
GAPC_GET_DEV_INFO_REQ_IND,
/// Send requested info to peer device
GAPC_GET_DEV_INFO_CFM,
/// Peer device request to modify local device info such as name or appearance
GAPC_SET_DEV_INFO_REQ_IND,
/// Local device accept or reject device info modification
GAPC_SET_DEV_INFO_CFM,
/* Connection parameters update */
/// Perform update of connection parameters command
GAPC_PARAM_UPDATE_CMD,
/// Request of updating connection parameters indication
GAPC_PARAM_UPDATE_REQ_IND,
/// Master confirm or not that parameters proposed by slave are accepted or not
GAPC_PARAM_UPDATE_CFM,
/// Connection parameters updated indication
GAPC_PARAM_UPDATED_IND,
/* Bonding procedure */
/// Start Bonding command procedure
GAPC_BOND_CMD,
/// Bonding requested by peer device indication message.
GAPC_BOND_REQ_IND,
/// Confirm requested bond information.
GAPC_BOND_CFM,
/// Bonding information indication message
GAPC_BOND_IND,
/* Encryption procedure */
/// Start Encryption command procedure
GAPC_ENCRYPT_CMD,
/// Encryption requested by peer device indication message.
GAPC_ENCRYPT_REQ_IND,
/// Confirm requested Encryption information.
GAPC_ENCRYPT_CFM,
/// Encryption information indication message
GAPC_ENCRYPT_IND,
/* Security request procedure */
/// Start Security Request command procedure
GAPC_SECURITY_CMD,
/// Security requested by peer device indication message
GAPC_SECURITY_IND,
/* Signature procedure */
/// Indicate the current sign counters to the application
GAPC_SIGN_COUNTER_IND,
/* Device information */
/// Indication of ongoing connection Channel Map
GAPC_CON_CHANNEL_MAP_IND,
/* Deprecated */
/// Deprecated messages
GAPC_DEPRECATED_0,
GAPC_DEPRECATED_1,
GAPC_DEPRECATED_2,
GAPC_DEPRECATED_3,
GAPC_DEPRECATED_4,
GAPC_DEPRECATED_5,
GAPC_DEPRECATED_6,
GAPC_DEPRECATED_7,
GAPC_DEPRECATED_8,
GAPC_DEPRECATED_9,
/* LE Ping */
/// Update LE Ping timeout value
GAPC_SET_LE_PING_TO_CMD,
/// LE Ping timeout indication
GAPC_LE_PING_TO_VAL_IND,
/// LE Ping timeout expires indication
GAPC_LE_PING_TO_IND,
/* LE Data Length extension*/
/// LE Set Data Length Command
GAPC_SET_LE_PKT_SIZE_CMD,
/// LE Set Data Length Indication
GAPC_LE_PKT_SIZE_IND,
/* Secure Connections */
/// Request to inform the remote device when keys have been entered or erased
GAPC_KEY_PRESS_NOTIFICATION_CMD,
/// Indication that a KeyPress has been performed on the peer device.
GAPC_KEY_PRESS_NOTIFICATION_IND,
/* LE PHY update */
/// Set the PHY configuration for current active link
GAPC_SET_PHY_CMD,
/// Active link PHY configuration. Triggered when configuration is read or during an update.
GAPC_LE_PHY_IND,
/* Connection parameters update - cont */
/// Set the preferred slave latency (for slave only, with RW controller)
GAPC_SET_PREF_SLAVE_LATENCY_CMD,
// ---------------------- INTERNAL API ------------------------
/* Internal messages for timer events, not part of API*/
/// Signature procedure
GAPC_SIGN_CMD,
/// Signature result
GAPC_SIGN_IND,
/// Parameter update procedure timeout indication
GAPC_PARAM_UPDATE_TO_IND,
/// Pairing procedure timeout indication
GAPC_SMP_TIMEOUT_TIMER_IND,
/// Pairing repeated attempts procedure timeout indication
GAPC_SMP_REP_ATTEMPTS_TIMER_IND,
GAPC_SET_MAX_RX_SIZE_AND_TIME_CMD,
};
比如收到了GAPC_GET_DEV_INFO_REQ_IND请求,消息ID来自与GAPC层,那么我们需要将设备信息返回:调用void GAPC_GetDevInfoCfm(uint8_t conidx, uint8_t req, const union gapc_dev_info_val* dat)
dat就是我们要返回的数据,函数内会打包成内核消息给蓝牙协议栈,最终发送出去。
conidx需要调用uint8_t conidx = KE_IDX_GET(src_id);获取
调试出现的问题
在调试模式下,运行此处代码,会解锁失败
/* ----------------------------------------------------------------------------
* Function : unsigned NVR2_WriteEnable(bool enable)
* ----------------------------------------------------------------------------
* Description : Enable/Disable writing to NVR2
* Inputs : - enable - Enable writing when true, disable otherwise
* Outputs : None
* Assumptions : Function should cause a watchdog reset in case of error
* ------------------------------------------------------------------------- */
void NVR2_WriteEnable(bool enable)
{
/* Lock or unlock NVR2 region */
FLASH->NVR_CTRL = NVR2_WRITE_ENABLE;
FLASH->NVR_WRITE_UNLOCK = enable ? FLASH_NVR_KEY : 0;
/* Check the lock/unlock operation result */
bool NVR2_unlocked = (FLASH->IF_STATUS & 0x3FF) == 0x20;
/* Error checking: this application needs to write in flash. If an error
* occurred while locking/unlocking NVR2, wait for a watchdog reset */
if (NVR2_unlocked != enable)
{
/* Disable all interrupts and clear any pending interrupts */
Sys_NVIC_DisableAllInt();
Sys_NVIC_ClearAllPendingInt();
//printf("[%s][%s][%s]error application needs to write in flash.", __FUNCTION__, __FILE__, __LINE__);
while (1)
{
/* Wait for Watchdog Reset to Occur */
__ASM("nop");
}
}
}
icf文件修改
/* The memory space denoting the maximum possible amount of addressable memory */
define memory Mem with size = 4G;
/* Memory regions in an address space */
define region ROM = Mem:[from 0x00000 size 4K];
define symbol __ICFEDIT_region_FLASH_start__ = 0x00100000;
define symbol __ICFEDIT_region_FLASH_end__ = __ICFEDIT_region_FLASH_start__+384*1024-1;
define region FLASH = Mem:[from __ICFEDIT_region_FLASH_start__ to __ICFEDIT_region_FLASH_end__];
define region FLASH_NVR_1 = Mem:[from 0x00080000 size 2K];
define region FLASH_NVR_2 = Mem:[from 0x00080800 size 2K];
define region PRAM = Mem:[from 0x00200000 size 32K];
define region PRAM_DSP = Mem:[from 0x00220000 size 40K];/*if use dsp just only for dsp*/
define region DRAM = Mem:[from 0x20000000 size 24K];
define region DRAM_DSP = Mem:[from 0x20006000 size 46K];
/*define region DRAM = Mem:[from 0x20000000 size 32K];
define region DRAM_DSP = Mem:[from 0x20008000 size 38K];*/
define region DRAM_DSP_SHARE = Mem:[from 0x20011800 size 2K];/*for dsp*/
define region DRAM_BB = Mem:[from 0x20012000 size 16K];
/* Create a stack */
define block CSTACK with size = 3K, alignment = 4 { };
define block HEAP with size = 3K, alignment = 4 { };
/* Handle initialization */
do not initialize { section .noinit, section .systemclock };
initialize by copy {readwrite}; /* Initialize RW sections, exclude zero-initialized sections */
/* Place startup code at the start of flash */
place at start of FLASH {readonly object startup_rsl10.o};
/* Place code and data */
place in FLASH {readonly}; /* Place constants and initializers: .rodata and .data_init */
/*place at end of FLASH {readonly section .dsp};*/
place in FLASH {readonly section .dsp};
place at start of DRAM {section .systemclock};
place in DRAM_DSP_SHARE {section .shared};/*for dsp*/
place in DRAM_DSP {readwrite}; /* Place .data, .bss, and .noinit */
place in DRAM {section .data, section .noinit, section .bss};
place at end of DRAM {block HEAP, block CSTACK};
place in PRAM {section .PRAM32Kmemory}; /* Place .PRAM32Kmemory */
define exported symbol FLASH_APP_Start_Addr = __ICFEDIT_region_FLASH_start__;
BLE-Asha样本工程移植进IAR
1、添加工程文件
2、增加接口文件
/**
* @file BLE_Port.c
*
* @date 2021/8/5
*
* @author aron566
*
* @copyright aron566.
*
* @brief 蓝牙驱动
*
* @details
*
* @version v1.0
*/
/** Includes -----------------------------------------------------------------*/
/* Private includes ----------------------------------------------------------*/
#include "BLE_Port.h"
#include <rsl10.h>
#include <rsl10_protocol.h>
#include <ble_gap.h>
#include <ble_gatt.h>
/*电量服务*/
#include <ble_bass.h>
#include <app_bass.h>
#include <ble_diss.h>
/*助听器*/
#include <ble_asha.h>
#include <msg_handler.h>
#include <printf.h>
/* use C compiler ------------------------------------------------------------*/
#ifdef __cplusplus ///<use C compiler
extern "C" {
#endif
/** Private typedef ----------------------------------------------------------*/
/** Private macros -----------------------------------------------------------*/
#define APP_IDX_MAX BLE_CONNECTION_MAX /* Number of APP Task Instances */
#define APP_BLE_DEV_PARAM_SOURCE FLASH_PROVIDED_or_DFLT /* or APP_PROVIDED */
/* If APP_BD_ADDRESS_TYPE == GAPM_CFG_ADDR_PUBLIC and APP_DEVICE_PARAM_SRC == FLASH_PROVIDED_or_DFLT
* the bluetooth address is loaded from FLASH NVR3. Otherwise, this address is used. */
#define APP_BD_ADDRESS_TYPE GAPM_CFG_ADDR_PRIVATE /* or GAPM_CFG_ADDR_PUBLIC*/
#define APP_BD_ADDRESS { 0x94, 0x11, 0x11, 0xff, 0xbb, 0xC2 }
#define APP_NB_PEERS 8 /* 1-8 */
#define OUTPUT_POWER_DBM 0 /* RF output power in dBm */
#define RADIO_CLOCK_ACCURACY 20 /* RF Oscillator accuracy in ppm */
#define UART_TX_BUFFER_SIZE 100
/* Keyboard UART CLI input codes */
#define KEY_ARROW_UP 65
#define KEY_ARROW_DOWN 66
#define KEY_ENTER 13
/* Maximum number of devices in scan list */
#define ADV_REPORT_LIST_MAX 30
/* Set scan interval to 62.5ms and scan window to 50% of the interval */
#define SCAN_INTERVAL 100
#define SCAN_WINDOW 50
/* Timeout to update the UART CLI */
#define SCAN_UART_CLI_TIMEOUT_MS 300
#define START_CONNECTION_CMD_TIMEOUT_MS 3000
/* The number of standard profiles and custom services added in this application */
#define APP_NUM_STD_PRF 2
#define APP_NUM_CUSTOM_SVC 0
/* Advertising data is composed by device name and company id */
#define APP_DEVICE_NAME_DEFAULT "ZX-Test"
/* Maximum length for the device name.
* This is limited by GAP_MAX_NAME_SIZE during the stack build. */
#define APP_DEVICE_NAME_MAXLEN 0x20
/*广播数据*/
/* Advertising data is composed by device name and company id */
#define APP_DEVICE_NAME "ZX-Test"
#define APP_DEVICE_NAME_LEN (sizeof(APP_DEVICE_NAME) - 1)
/*SERVICE 0000ff00-0000-1000-8000-00805f9b34fb*/
/*NOTIFY 0000ff01-0000-1000-8000-00805f9b34fb*/
/*WRITE 0000ff01-0000-1000-8000-00805f9b34fb*/
/* Manufacturer info (ON SEMICONDUCTOR Company ID) */
#define APP_COMPANY_ID {0x62, 0x3}
#define APP_COMPANY_ID_LEN 2
#define APP_DEVICE_APPEARANCE_DEFAULT 0
#define APP_PREF_SLV_MIN_CON_INTERVAL 8
#define APP_PREF_SLV_MAX_CON_INTERVAL 10
#define APP_PREF_SLV_LATENCY 0
#define APP_PREF_SLV_SUP_TIMEOUT 200
/* --------------------------------------------------------------------------
* Device Information used for Device Information Server Service (DISS)
* ----------------------------------------------------------------------- */
/* Manufacturer Name Value */
#define APP_DIS_MANUFACTURER_NAME ("dev_name")
#define APP_DIS_MANUFACTURER_NAME_LEN (8)
/* Model Number String Value */
#define APP_DIS_MODEL_NB_STR ("RW-BLE-1.0")
#define APP_DIS_MODEL_NB_STR_LEN (10)
/* Serial Number */
#define APP_DIS_SERIAL_NB_STR ("1.0.0.0-LE")
#define APP_DIS_SERIAL_NB_STR_LEN (10)
/* Firmware Revision */
#define APP_DIS_FIRM_REV_STR ("1.0.2")
#define APP_DIS_FIRM_REV_STR_LEN (5)
/* System ID Value - LSB -> MSB */
#define APP_DIS_SYSTEM_ID ("\x12\x34\x56\xFF\xFE\x9A\xBC\xDE")
#define APP_DIS_SYSTEM_ID_LEN (8)
/* Hardware Revision String */
#define APP_DIS_HARD_REV_STR ("1.0.0")
#define APP_DIS_HARD_REV_STR_LEN (5)
/* Software Revision String */
#define APP_DIS_SW_REV_STR ("1.0.0")
#define APP_DIS_SW_REV_STR_LEN (5)
/* IEEE */
#define APP_DIS_IEEE ("\xFF\xEE\xDD\xCC\xBB\xAA")
#define APP_DIS_IEEE_LEN (6)
/**
* PNP ID Value - LSB -> MSB
* Vendor ID Source : 0x02 (USB Implementerӳ Forum assigned Vendor ID value)
* Vendor ID : 0x1057 (ON Semiconductor)
* Product ID : 0x0040
* Product Version : 0x0300
*/
#define APP_DIS_PNP_ID ("\x02\x57\x10\x40\x00\x00\x03")
#define APP_DIS_PNP_ID_LEN (7)
#define APP_DIS_FEATURES (DIS_ALL_FEAT_SUP)
/* Application-provided IRK */
#define APP_IRK { 0x01, 0x23, 0x45, 0x68, 0x78, 0x9a, \
0xbc, 0xde, 0x01, 0x23, 0x45, 0x68, \
0x78, 0x9a, 0xbc, 0xde }
/* Application-provided CSRK */
#define APP_CSRK { 0x01, 0x23, 0x45, 0x68, 0x78, 0x9a, \
0xbc, 0xde, 0x01, 0x23, 0x45, 0x68, \
0x78, 0x9a, 0xbc, 0xde }
/* Application-provided private key */
#define APP_PRIVATE_KEY { 0xEC, 0x89, 0x3C, 0x11, 0xBB, 0x2E, \
0xEB, 0x5C, 0x80, 0x88, 0x63, 0x57, \
0xCC, 0xE2, 0x05, 0x17, 0x20, 0x75, \
0x5A, 0x26, 0x3E, 0x8D, 0xCF, 0x26, \
0x63, 0x1D, 0x26, 0x0B, 0xCE, 0x4D, \
0x9E, 0x07 }
/* Application-provided public key X */
#define APP_PUBLIC_KEY_X { 0x56, 0x09, 0x79, 0x1D, 0x5A, 0x5F, \
0x4A, 0x5C, 0xFE, 0x89, 0x56, 0xEC, \
0xE6, 0xF7, 0x92, 0x21, 0xAC, 0x93, \
0x99, 0x10, 0x51, 0x82, 0xF4, 0xDD, \
0x84, 0x07, 0x50, 0x99, 0xE7, 0xC2, \
0xF1, 0xC8 }
/* Application-provided public key Y */
#define APP_PUBLIC_KEY_Y { 0x40, 0x84, 0xB4, 0xA6, 0x08, 0x67, \
0xFD, 0xAC, 0x81, 0x5D, 0xB0, 0x41, \
0x27, 0x75, 0x9B, 0xA7, 0x92, 0x57, \
0x0C, 0x44, 0xB1, 0x57, 0x7C, 0x76, \
0x5B, 0x56, 0xF0, 0xBA, 0x03, 0xF4, \
0xAA, 0x67 }
/* PEriod that only clients that were previously bonded can connect */
#define APP_WHITELIST_PERIOD_S 10
/*蓝牙助听器*/
/* ASHA implementation-specific definitions */
#define APP_ASHA_HI_SYNC_ID {0x62, 0x03, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}
#define APP_ASHA_DEVICE_CAPABILITIES (ASHA_CAPABILITIES_MONAURAL | ASHA_CAPABILITIES_SIDE)
#define APP_ASHA_SUPPORTED_CODECS ASHA_CODEC_ID_G722_16KHZ
#define APP_ASHA_RENDER_DELAY 0 //0ms
#define APP_ASHA_PREPARATION_DELAY 0 //0ms
#define APP_ASHA_FEATURE_MAP ASHA_FEATURE_MAP_LE_COC_SUPPORTED
#define APP_ASHA_VERSION 0x01
/** Private constants --------------------------------------------------------*/
static const struct ke_msg_handler appm_default_state[] =
{
{ KE_MSG_DEFAULT_HANDLER, (ke_msg_func_t)MsgHandler_Notify }
};
/* Use the state and event handler definition for all states. */
static const struct ke_state_handler appm_default_handler
= KE_STATE_HANDLER(appm_default_state);
/* Defines a place holder for all task instance's state */
ke_state_t appm_state[APP_IDX_MAX];
static const struct ke_task_desc TASK_DESC_APP = {
NULL, &appm_default_handler,
appm_state, 0,
APP_IDX_MAX
};
const struct ReadOnlyProperties_t ashaReadOnlyProperties = {
.version = APP_ASHA_VERSION,
.deviceCapabilities = APP_ASHA_DEVICE_CAPABILITIES,
.hiSyncId = APP_ASHA_HI_SYNC_ID,
.featureMap = APP_ASHA_FEATURE_MAP,
.renderDelay = APP_ASHA_RENDER_DELAY,
.preparationDelay = APP_ASHA_PREPARATION_DELAY, //TODO: change to reserved
.codecIDs = APP_ASHA_SUPPORTED_CODECS,
};
/** Public variables ---------------------------------------------------------*/
/* Device Information structure initialization, includes length and data string */
const struct DISS_DeviceInfo_t deviceInfo =
{
.MANUFACTURER_NAME = {.len = APP_DIS_MANUFACTURER_NAME_LEN, .data = (uint8_t*) APP_DIS_MANUFACTURER_NAME},
.MODEL_NB_STR = {.len = APP_DIS_MODEL_NB_STR_LEN, .data = (uint8_t*) APP_DIS_MODEL_NB_STR},
.SERIAL_NB_STR = {.len = APP_DIS_SERIAL_NB_STR_LEN, .data = (uint8_t*) APP_DIS_SERIAL_NB_STR},
.FIRM_REV_STR = {.len = APP_DIS_FIRM_REV_STR_LEN, .data = (uint8_t*) APP_DIS_FIRM_REV_STR},
.SYSTEM_ID = {.len = APP_DIS_SYSTEM_ID_LEN, .data = (uint8_t*) APP_DIS_SYSTEM_ID},
.HARD_REV_STR = {.len = APP_DIS_HARD_REV_STR_LEN, .data = (uint8_t*) APP_DIS_HARD_REV_STR},
.SW_REV_STR = {.len = APP_DIS_SW_REV_STR_LEN, .data = (uint8_t*) APP_DIS_SW_REV_STR},
.IEEE = {.len = APP_DIS_IEEE_LEN, .data = (uint8_t*) APP_DIS_IEEE},
.PNP = {.len = APP_DIS_PNP_ID_LEN, .data = (uint8_t*) APP_DIS_PNP_ID},
};
struct gapm_set_dev_config_cmd devConfigCmd =
{
.operation = GAPM_SET_DEV_CONFIG,
.role = GAP_ROLE_PERIPHERAL,
.renew_dur = GAPM_DEFAULT_RENEW_DUR,
.addr.addr = APP_BD_ADDRESS,
.irk.key = APP_IRK,
.addr_type = APP_BD_ADDRESS_TYPE,
#ifdef SECURE_CONNECTION
.pairing_mode = (GAPM_PAIRING_SEC_CON | GAPM_PAIRING_LEGACY),
#else
.pairing_mode = GAPM_PAIRING_LEGACY,
#endif
.gap_start_hdl = GAPM_DEFAULT_GAP_START_HDL,
.gatt_start_hdl = GAPM_DEFAULT_GATT_START_HDL,
.att_and_ext_cfg = GAPM_DEFAULT_ATT_CFG,
.sugg_max_tx_octets = GAPM_DEFAULT_TX_OCT_MAX,
.sugg_max_tx_time = GAPM_DEFAULT_TX_TIME_MAX,
.max_mtu = GAPM_DEFAULT_MTU_MAX,
.max_mps = GAPM_DEFAULT_MPS_MAX,
.max_nb_lecb = L2C_CONNECTION_MAX,
.audio_cfg = GAPM_DEFAULT_AUDIO_CFG,
.tx_pref_rates = GAP_RATE_LE_1MBPS,
.rx_pref_rates = GAP_RATE_LE_1MBPS
};
/*广播模式*/
struct gapm_start_advertise_cmd advertiseCmd =
{
.op = {
.code = GAPM_ADV_UNDIRECT,
.addr_src = GAPM_STATIC_ADDR,
.state = 0
},
.intv_min = GAPM_DEFAULT_ADV_INTV_MIN,
.intv_max = GAPM_DEFAULT_ADV_INTV_MAX,
.channel_map = GAPM_DEFAULT_ADV_CHMAP,
.info.host = {
.mode = GAP_GEN_DISCOVERABLE,
.adv_filt_policy = ADV_ALLOW_SCAN_ANY_CON_ANY
/* ADV_DATA and SCAN_RSP data are set in APP_BLE_Initialize() */
}
};
/*设备名称信息*/
const union gapc_dev_info_val getDevInfoCfmName =
{
.name.length = APP_DEVICE_NAME_LEN,
.name.value = {APP_DEVICE_NAME}
};
/*设备外观图标默认为0,非通用设备*/
const union gapc_dev_info_val getDevInfoCfmAppearance =
{
.appearance = APP_DEVICE_APPEARANCE_DEFAULT
};
/*通讯时延参数*/
const union gapc_dev_info_val getDevInfoCfmSlvParams =
{
.slv_params = {APP_PREF_SLV_MIN_CON_INTERVAL,
APP_PREF_SLV_MAX_CON_INTERVAL,
APP_PREF_SLV_LATENCY,
APP_PREF_SLV_SUP_TIMEOUT}
};
/*设备配置信息集合*/
const union gapc_dev_info_val* getDevInfoCfm[] =
{
[GAPC_DEV_NAME] = &getDevInfoCfmName,
[GAPC_DEV_APPEARANCE] = &getDevInfoCfmAppearance,
[GAPC_DEV_SLV_PREF_PARAMS] = &getDevInfoCfmSlvParams
};
/** Private variables --------------------------------------------------------*/
/** Private function prototypes ----------------------------------------------*/
/** Private user code --------------------------------------------------------*/
/** Private application code -------------------------------------------------*/
/*******************************************************************************
*
* Static code
*
********************************************************************************
*/
/**
******************************************************************
* @brief 初始化时钟
* @param [in]None.
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/12
******************************************************************
*/
static inline void BLE_Port_Clock_Init(void)
{
/* Configure the current trim settings for VCC, VDDA */
ACS_VCC_CTRL->ICH_TRIM_BYTE = VCC_ICHTRIM_16MA_BYTE;
ACS_VDDA_CP_CTRL->PTRIM_BYTE = VDDA_PTRIM_16MA_BYTE;
/* Disable RF TX power amplifier supply voltage and
* connect the switched output to VDDRF regulator */
ACS_VDDPA_CTRL->ENABLE_ALIAS = VDDPA_DISABLE_BITBAND;
ACS_VDDPA_CTRL->VDDPA_SW_CTRL_ALIAS = VDDPA_SW_VDDRF_BITBAND;
/* Set radio clock accuracy in ppm */
BLE_DeviceParam_Set_ClockAccuracy(RADIO_CLOCK_ACCURACY);
/*8MHz / 8*/
BBIF->CTRL = (BB_CLK_ENABLE | BBCLK_DIVIDER_8 | BB_WAKEUP);
/* Seed the random number generator */
srand(1);
/* Initialize the kernel and Bluetooth stack */
Kernel_Init(0);
BLE_InitNoTL(0);
/* Set radio output power of RF */
Sys_RFFE_SetTXPower(OUTPUT_POWER_DBM);
/* Create the application task handler */
ke_task_create(TASK_APP, &TASK_DESC_APP);
/* Enable Bluetooth related interrupts */
NVIC->ISER[1] = (NVIC_BLE_CSCNT_INT_ENABLE |
NVIC_BLE_SLP_INT_ENABLE |
NVIC_BLE_RX_INT_ENABLE |
NVIC_BLE_EVENT_INT_ENABLE |
NVIC_BLE_CRYPT_INT_ENABLE |
NVIC_BLE_ERROR_INT_ENABLE |
NVIC_BLE_GROSSTGTIM_INT_ENABLE |
NVIC_BLE_FINETGTIM_INT_ENABLE |
NVIC_BLE_SW_INT_ENABLE);
}
/* ----------------------------------------------------------------------------
* Function : void BASS_Setup(void)
* ----------------------------------------------------------------------------
* Description : Configure the Battery Service Server
* Inputs : None
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
static void BASS_Setup(void)
{
BASS_Initialize(APP_BAS_NB, APP_BASS_ReadBatteryLevel);
BASS_NotifyOnBattLevelChange(TIMER_SETTING_S(1)); /* Periodically monitor the battery level. Only notify changes */
APP_BASS_SetBatMonAlarm(BATMON_SUPPLY_THRESHOLD_CFG); /* BATMON alarm configuration */
}
/* ----------------------------------------------------------------------------
* Function : void DISS_Setup(void)
* ----------------------------------------------------------------------------
* Description : Configure the Device Information Service Server
* Inputs : None
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
/**
* @brief Configure the Device Information Service Server
*/
static void DISS_Setup(void)
{
DISS_Initialize(APP_DIS_FEATURES, (const struct DISS_DeviceInfo_t*) &deviceInfo);
}
/**
******************************************************************
* @brief 设置广播数据
* @param [in]None
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/24
******************************************************************
*/
static void APP_SetAdvScanData(void)
{
uint8_t devName[] = APP_DEVICE_NAME;
uint8_t asha_uuid[] = ASHA_SERVICE_UUID;
uint8_t companyID[] = APP_COMPANY_ID;
/* Set advertising data as device name + ASHA UUID */
advertiseCmd.info.host.adv_data_len = 0;
/*设置完整设备名*/
GAPM_AddAdvData(GAP_AD_TYPE_COMPLETE_NAME, devName,
APP_DEVICE_NAME_LEN, advertiseCmd.info.host.adv_data,
&advertiseCmd.info.host.adv_data_len);
/*设置16BIT 服务UUID 公有*/
GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, asha_uuid,
sizeof(asha_uuid), advertiseCmd.info.host.scan_rsp_data,
&advertiseCmd.info.host.scan_rsp_data_len);
/* Set scan response data as company ID + ASHA UUID */
advertiseCmd.info.host.scan_rsp_data_len = 0;
/*设置厂家ID*/
GAPM_AddAdvData(GAP_AD_TYPE_MANU_SPECIFIC_DATA, companyID,
APP_COMPANY_ID_LEN, advertiseCmd.info.host.adv_data,
&advertiseCmd.info.host.adv_data_len);
/*设置16BIT 服务UUID 公有*/
GAPM_AddAdvData(GAP_AD_TYPE_SERVICE_16_BIT_DATA, asha_uuid,
sizeof(asha_uuid), advertiseCmd.info.host.scan_rsp_data,
&advertiseCmd.info.host.scan_rsp_data_len);
}
/**
******************************************************************
* @brief 设置通讯参数
* @param [in]conidx 任务ID
* @param [in]cfm 参数
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/24
******************************************************************
*/
static void APP_SetConnectionCfmParams(uint8_t conidx, struct gapc_connection_cfm* cfm)
{
cfm->svc_changed_ind_enable = 0;
cfm->ltk_present = false;
#ifdef SECURE_CONNECTION
cfm->pairing_lvl = GAP_PAIRING_BOND_SECURE_CON;
#else
cfm->pairing_lvl = GAP_PAIRING_BOND_UNAUTH;
#endif
#if CFG_BOND_LIST_IN_NVR2
if(GAPC_IsBonded(conidx))
{
cfm->ltk_present = true;
Device_Param_Read(PARAM_ID_CSRK, cfm->lcsrk.key);
memcpy(cfm->rcsrk.key, GAPC_GetBondInfo(conidx)->CSRK, KEY_LEN);
cfm->lsign_counter = 0xFFFFFFFF;
cfm->rsign_counter = 0;
}
#endif
PRINTF(" connectionCfm->ltk_present = %d \n\r", cfm->ltk_present);
}
/* ----------------------------------------------------------------------------
* Function : Device_Param_Prepare(struct app_device_param * param)
* ----------------------------------------------------------------------------
* Description : This function allows the application to overwrite a few BLE
* parameters (BD address and keys) without having to write
* data into RSL10 flash (NVR3). This function is called by the
* stack and it's useful for debugging and testing purposes.
* Inputs : - param - pointer to the parameters to be configured
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void Device_Param_Prepare(app_device_param_t *param)
{
param->device_param_src_type = APP_BLE_DEV_PARAM_SOURCE;
if(param->device_param_src_type == APP_PROVIDED)
{
uint8_t temp_bleAddress[6] = APP_BD_ADDRESS;
memcpy(param->bleAddress, temp_bleAddress, 6);
}
}
#include <app_audio.h>
extern const struct att_db_desc asha_att_db[];
extern audio_frame_param_t env_audio;
/* ----------------------------------------------------------------------------
* Function : void APP_ASHA_CallbackHandler(enum ASHA_Operation_t op, void *param)
* ----------------------------------------------------------------------------
* Description : Application callback function to interact with ble_asha
* service. Handle ASHA events to start/stop streaming or
* change volume.
* Inputs : - op - Kernel message ID number
* - param - Operation parameter to be type casted to:
* - For op == ASHA_VOLUME_CHANGE,
* param == struct asha_volume_change*
* - For op == ASHA_AUDIO_START,
* param == struct asha_audio_start*
* - For op == ASHA_AUDIO_STOP,
* param == NULL
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void APP_ASHA_CallbackHandler(enum ASHA_Operation_t op, void *param)
{
switch(op)
{
case ASHA_VOLUME_CHANGE:
{
struct asha_volume_change* p = param;
Volume_Set(p->volume);
PRINTF("\r\nAPP_ASHA_CallbackHandler - ASHA_VOLUME_CHANGE: volume=%d\r\n", p->volume);
}
break;
case ASHA_AUDIO_START:
{
struct asha_audio_start* p = param;
Volume_Set(p->volume);
APP_ResetPrevSeqNumber();
PRINTF("\r\nAPP_ASHA_CallbackHandler - ASHA_AUDIO_START: codec=%d audiotype=%d volume=%d\r\n",
p->codec, p->audiotype, p->volume);
if( env_audio.state == LINK_DISCONNECTED )
{
/* We don't directly start the rendering IRQs, we'll delay that
* to after we received an audio packet */
env_audio.state = LINK_TRANSIENT;
}
}
break;
case ASHA_AUDIO_STOP:
{
PRINTF("\r\nAPP_ASHA_CallbackHandler - ASHA_AUDIO_STOP\r\n");
APP_Audio_Disconnect();
}
break;
case ASHA_AUDIO_RCVD:
{
struct asha_audio_received *rcv_p = (struct asha_audio_received *) param;
// PRINTF("\r\nAPP_ASHA_CallbackHandler - ASHA_AUDIO_RCVD\r\n");
APP_Audio_Transfer(rcv_p->data, rcv_p->length - 1, rcv_p->seq_number);
}
break;
case ASHA_AUDIO_STATUS:
{
PRINTF("\r\nAPP_ASHA_CallbackHandler - ASHA_AUDIO_STATUS. Other peripheral state: %d\r\n", ((struct asha_audio_status *) param)->connected);
}
break;
}
}
/* ----------------------------------------------------------------------------
* Function : void APP_GAPM_GATTM_Handler(ke_msg_id_t const msg_id,
* void const *param,
* ke_task_id_t const dest_id,
* ke_task_id_t const src_id)
* ----------------------------------------------------------------------------
* Description : Handle GAPM/GATTM messages that need application action
* Inputs : - msg_id - Kernel message ID number
* - param - Message parameter
* - dest_id - Destination task ID number
* - src_id - Source task ID number
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void APP_GAPM_GATTM_Handler(ke_msg_id_t const msg_id, void const *param,
ke_task_id_t const dest_id,
ke_task_id_t const src_id)
{
switch(msg_id)
{
case GAPM_CMP_EVT:
{
const struct gapm_cmp_evt *p = param;
/* Reset completed. Apply device configuration. */
if(p->operation == GAPM_RESET)
{
GAPM_SetDevConfigCmd(&devConfigCmd);
/* Trigger a GAPM_CMP_EVT / GAPM_SET_DEV_CONFIG when finished.
* BASS (ble_bass.c) and CUSTOMSS (app_customss.c) monitor this
* event before adding the profile/service to the stack. */
}
else if(p->operation == GAPM_SET_DEV_CONFIG && p->status == GAP_ERR_NO_ERROR)
{
/* Add custom services' attribute database (in this application only ASHA service) */
GATTM_AddAttributeDatabase(asha_att_db, ASHA_MAX_IDX);
}
else if((p->operation == GAPM_RESOLV_ADDR) && /* IRK not found for address */
(p->status == GAP_ERR_NOT_FOUND))
{
struct gapc_connection_cfm cfm;
uint8_t conidx = KE_IDX_GET(dest_id);
APP_SetConnectionCfmParams(conidx, &cfm);
GAPC_ConnectionCfm(conidx, &cfm); /* Confirm connection without LTK. */
PRINTF("\r\nGAPM_CMP_EVT / GAPM_RESOLV_ADDR. conidx=%d Status = NOT FOUND", conidx);
}
}
break;
case GATTM_ADD_SVC_RSP:
case GAPM_PROFILE_ADDED_IND:
{
/* If all expected profiles/services have been added */
if(GAPM_GetProfileAddedCount() == APP_NUM_STD_PRF &&
GATTM_GetServiceAddedCount() == APP_NUM_CUSTOM_SVC)
{
GAPM_StartAdvertiseCmd(&advertiseCmd); /* Start advertising */
/* Start the LED periodic timer. LED blinks according to the
* number of connected peers. See APP_LED_Timeout_Handler. */
ke_timer_set(APP_LED_TIMEOUT, TASK_APP, TIMER_SETTING_MS(200));
}
}
break;
case GAPM_ADDR_SOLVED_IND: /* Private address resolution was successful */
{
PRINTF("\r\nGAPM_ADDR_SOLVED_IND");
struct gapc_connection_cfm cfm;
uint8_t conidx = KE_IDX_GET(dest_id);
APP_SetConnectionCfmParams(conidx, &cfm);
GAPC_ConnectionCfm(conidx, &cfm); /* Send connection confirmation with LTK */
}
break;
}
}
/* ----------------------------------------------------------------------------
* Function : void APP_GAPC_MsgHandler(ke_msg_id_t const msg_id,
* void const *param,
* ke_task_id_t const dest_id,
* ke_task_id_t const src_id)
* ----------------------------------------------------------------------------
* Description : Handle GAPC messages that need application action
* Inputs : - msg_id - Kernel message ID number
* - param - Message parameter
* - dest_id - Destination task ID number
* - src_id - Source task ID number
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void APP_GAPC_Handler(ke_msg_id_t const msg_id, void const *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
uint8_t conidx = KE_IDX_GET(src_id);
switch(msg_id)
{
case GAPC_CONNECTION_REQ_IND:
{
const struct gapc_connection_req_ind* p = param;
PRINTF("\r\nGAPC_CONNECTION_REQ_IND: con_interval=%d, con_latency = %d, sup_to = %d, clk_accuracy = %d",
p->con_interval,
p->con_latency,
p->sup_to,
p->clk_accuracy);
PRINTF("\r\n ADDR: ");
for (uint8_t i = 0;i < GAP_BD_ADDR_LEN; i++)
PRINTF("%d ", p->peer_addr.addr[i]);
PRINTF("\r\n ADDR_TYPE: %d", p->peer_addr_type);
#if CFG_BOND_LIST_IN_NVR2
if(GAP_IsAddrPrivateResolvable(p->peer_addr.addr, p->peer_addr_type) &&
BondList_Size() > 0)
{
PRINTF("\r\n Starting GAPM_ResolvAddrCmd\r\n");
GAPM_ResolvAddrCmd(conidx, p->peer_addr.addr, 0, NULL);
}
else
#endif
{
struct gapc_connection_cfm cfm;
APP_SetConnectionCfmParams(conidx, &cfm);
GAPC_ConnectionCfm(conidx, &cfm); /* Send connection confirmation */
}
/* If not yet connected to all peers, keep advertising */
if((GAPC_GetConnectionCount() < APP_NB_PEERS))
{
GAPM_StartAdvertiseCmd(&advertiseCmd);
}
}
break;
case GAPC_DISCONNECT_IND:
{
GAPM_StartAdvertiseCmd(&advertiseCmd);
APP_Audio_Disconnect();
PRINTF("\r\nGAPC_DISCONNECT_IND: reason = %d", ((struct gapc_disconnect_ind*)param)->reason);
}
break;
case GAPC_GET_DEV_INFO_REQ_IND:
{
const struct gapc_get_dev_info_req_ind* p = param;
GAPC_GetDevInfoCfm(conidx, p->req, getDevInfoCfm[p->req]);
PRINTF("\r\nGAPC_GET_DEV_INFO_REQ_IND: req = %d", p->req);
}
break;
case GAPC_PARAM_UPDATE_REQ_IND:
{
GAPC_ParamUpdateCfm(conidx, true, 0xFFFF, 0xFFFF);
PRINTF("\r\nGAPC_PARAM_UPDATE_REQ_IND: intv_min=%d, intv_max = %d, latency = %d, time_out = %d",
((struct gapc_conn_param*) param)->intv_min,
((struct gapc_conn_param*) param)->intv_max,
((struct gapc_conn_param*) param)->latency,
((struct gapc_conn_param*) param)->time_out);
}
break;
case GAPC_PARAM_UPDATED_IND:
{
struct gapc_set_pref_slave_latency_cmd *cmd = KE_MSG_ALLOC(GAPC_SET_PREF_SLAVE_LATENCY_CMD,
KE_BUILD_ID(TASK_GAPC, conidx), TASK_APP, gapc_set_pref_slave_latency_cmd);
cmd->operation = GAPC_SET_PREF_SLAVE_LATENCY;
cmd->latency = 0;
ke_msg_send(cmd);
}
break;
#if CFG_BOND_LIST_IN_NVR2
case GAPC_ENCRYPT_REQ_IND:
{
const struct gapc_encrypt_req_ind* p = param;
/* Accept request if bond information is valid & EDIV/RAND match */
bool found = (GAPC_IsBonded(conidx) &&
p->ediv == GAPC_GetBondInfo(conidx)->EDIV &&
!memcmp(p->rand_nb.nb, GAPC_GetBondInfo(conidx)->RAND, GAP_RAND_NB_LEN));
PRINTF("\r\nGAPC_ENCRYPT_REQ_IND: bond information %s", (found ? "FOUND" : "NOT FOUND"));
PRINTF("\r\n GAPC_isBonded=%d GAPC_EDIV=%d GAPC_rand= %d %d %d %d ",
GAPC_IsBonded(conidx), GAPC_GetBondInfo(conidx)->EDIV,
GAPC_GetBondInfo(conidx)->RAND[0], GAPC_GetBondInfo(conidx)->RAND[1],
GAPC_GetBondInfo(conidx)->RAND[2], GAPC_GetBondInfo(conidx)->RAND[3]);
PRINTF("\r\n p_EDIV=%d p_rand= %d %d %d %d ", p->ediv,
p->rand_nb.nb[0], p->rand_nb.nb[1], p->rand_nb.nb[2], p->rand_nb.nb[3]);
GAPC_EncryptCfm(conidx, found, GAPC_GetBondInfo(conidx)->LTK);
}
break;
case GAPC_ENCRYPT_IND:
{
PRINTF("\r\nGAPC_ENCRYPT_IND: Link encryption is ON");
}
break;
case GAPC_BOND_REQ_IND:
{
const struct gapc_bond_req_ind* p = param;
switch (p->request)
{
case GAPC_PAIRING_REQ:
{
bool accept = BondList_Size() < APP_BONDLIST_SIZE;
union gapc_bond_cfm_data pairingRsp =
{
.pairing_feat =
{
.iocap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT,
.oob = GAP_OOB_AUTH_DATA_NOT_PRESENT,
.key_size = KEY_LEN,
.ikey_dist = (GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY),
.rkey_dist = (GAP_KDIST_ENCKEY | GAP_KDIST_IDKEY | GAP_KDIST_SIGNKEY),
}
};
#ifdef SECURE_CONNECTION
if (p->data.auth_req & GAP_AUTH_SEC_CON)
{
pairingRsp.pairing_feat.auth = GAP_AUTH_REQ_SEC_CON_BOND;
pairingRsp.pairing_feat.sec_req = GAP_SEC1_SEC_CON_PAIR_ENC;
}
else
#endif /* ifdef SECURE_CONNECTION */
{
pairingRsp.pairing_feat.auth = GAP_AUTH_REQ_NO_MITM_BOND;
pairingRsp.pairing_feat.sec_req = GAP_NO_SEC;
}
PRINTF("\r\nGAPC_BOND_REQ_IND / GAPC_PAIRING_REQ: accept = %d conidx=%d", accept, conidx);
GAPC_BondCfm(conidx, GAPC_PAIRING_RSP, accept, &pairingRsp);
}
break;
case GAPC_LTK_EXCH: /* Prepare and send random LTK (legacy only) */
{
PRINTF("\r\nGAPC_BOND_REQ_IND / GAPC_LTK_EXCH");
union gapc_bond_cfm_data ltkExch;
ltkExch.ltk.ediv = co_rand_hword();
for(uint8_t i = 0, i2 = GAP_RAND_NB_LEN; i < GAP_RAND_NB_LEN; i++, i2++)
{
ltkExch.ltk.randnb.nb[i] = co_rand_byte();
ltkExch.ltk.ltk.key[i] = co_rand_byte();
ltkExch.ltk.ltk.key[i2] = co_rand_byte();
}
GAPC_BondCfm(conidx, GAPC_LTK_EXCH, true, <kExch); /* Send confirmation */
}
break;
case GAPC_TK_EXCH: /* Prepare and send TK */
{
PRINTF("\r\nGAPC_BOND_REQ_IND / GAPC_TK_EXCH");
/* IO Capabilities are set to GAP_IO_CAP_NO_INPUT_NO_OUTPUT in this application.
* Therefore TK exchange is NOT performed. It is always set to 0 (Just Works algorithm). */
}
break;
case GAPC_IRK_EXCH:
{
PRINTF("\r\nGAPC_BOND_REQ_IND / GAPC_IRK_EXCH");
union gapc_bond_cfm_data irkExch;
memcpy(irkExch.irk.addr.addr.addr, GAPM_GetDeviceConfig()->addr.addr, GAP_BD_ADDR_LEN);
irkExch.irk.addr.addr_type = GAPM_GetDeviceConfig()->addr_type;
memcpy(irkExch.irk.irk.key, GAPM_GetDeviceConfig()->irk.key, GAP_KEY_LEN);
GAPC_BondCfm(conidx, GAPC_IRK_EXCH, true, &irkExch); /* Send confirmation */
}
break;
case GAPC_CSRK_EXCH:
{
PRINTF("\r\nGAPC_BOND_REQ_IND / GAPC_CSRK_EXCH");
union gapc_bond_cfm_data csrkExch;
Device_Param_Read(PARAM_ID_CSRK, csrkExch.csrk.key);
GAPC_BondCfm(conidx, GAPC_CSRK_EXCH, true, &csrkExch); /* Send confirmation */
}
break;
}
}
break;
#endif /* CFG_BOND_LIST_IN_NVR2 */
}
}
/* ----------------------------------------------------------------------------
* Function : void APP_LED_Timeout_Handler(ke_msg_idd_t const msg_id,
* void const *param,
* ke_task_id_t const dest_id,
* ke_task_id_t const src_id)
* ----------------------------------------------------------------------------
* Description : Control GPIO "LED_DIO_NUM" behavior using a timer.
* Possible LED behaviors:
* - If the device is advertising but it has not connected
* to any peer: the LED blinks every 200 ms.
* - If the device is advertising and it is connecting to
* fewer than BLE_CONNECTION_MAX peers: the LED blinks
* every 2 seconds according to the number of connected
* peers (i.e., blinks once if one peer is connected,
* twice if two peers are connected, etc.).
* - If the device is connected to BLE_CONNECTION_MAX peers
* the LED is steady on.
* Inputs : - msg_id - Kernel message ID number
* - param - Message parameter (unused)
* - dest_id - Destination task ID number
* - src_id - Source task ID number
* Outputs : None
* Assumptions : None
* ------------------------------------------------------------------------- */
void APP_LED_Timeout_Handler(ke_msg_id_t const msg_id, void const *param,
ke_task_id_t const dest_id, ke_task_id_t const src_id)
{
static uint8_t toggle_cnt = 0;
uint8_t connectionCount = GAPC_GetConnectionCount();
/* Blink LED according to the number of connections */
switch (connectionCount)
{
case 0:
{
ke_timer_set(APP_LED_TIMEOUT, TASK_APP, TIMER_SETTING_MS(200));
Sys_GPIO_Toggle(LED_DIO_NUM); /* Toggle LED_DIO_NUM every 200ms */
toggle_cnt = 0;
}
break;
case APP_NB_PEERS:
{
ke_timer_set(APP_LED_TIMEOUT, TASK_APP, TIMER_SETTING_MS(200));
Sys_GPIO_Set_High(LED_DIO_NUM); /* LED_DIO_NUM steady high */
toggle_cnt = 0;
}
break;
default: /* connectionCount is between 1 and APP_NB_PEERS (exclusive) */
{
if (toggle_cnt >= connectionCount * 2)
{
toggle_cnt = 0;
ke_timer_set(APP_LED_TIMEOUT, TASK_APP, TIMER_SETTING_S(2)); /* Schedule timer for a long 2s break */
Sys_GPIO_Set_High(LED_DIO_NUM); /* LED_DIO_NUM steady high until next 2s blinking period */
}
else
{
toggle_cnt++;
Sys_GPIO_Toggle(LED_DIO_NUM);
ke_timer_set(APP_LED_TIMEOUT, TASK_APP, TIMER_SETTING_MS(200));
}
}
}
}
/** Public application code --------------------------------------------------*/
/*******************************************************************************
*
* Public code
*
********************************************************************************
*/
/**
******************************************************************
* @brief 清空绑定列表
* @param [in]None.
* @return true 成功.
* @author aron566
* @version v1.0
* @date 2021/8/24
******************************************************************
*/
bool BLE_Port_BondList_RemoveAll(void)
{
/* Run the following command when erasing flash/bond_list is desirable */
return BondList_RemoveAll();
}
/**
******************************************************************
* @brief 蓝牙协议栈启动
* @param [in]None.
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/13
******************************************************************
*/
void BLE_Port_Start(void)
{
/*执行事件调度*/
Kernel_Schedule();
}
/**
******************************************************************
* @brief 蓝牙初始化
* @param [in]None.
* @return None.
* @author aron566
* @version v1.0
* @date 2021/8/5
******************************************************************
*/
void BLE_Port_Init(void)
{
/*蓝牙时钟初始化*/
BLE_Port_Clock_Init();
/*初始化音频蓝牙 */
Audio_Initialize_System();
/* Enable Flash overlay */
memcpy((uint8_t *)PRAM0_BASE, (uint8_t *)FLASH_MAIN_BASE, PRAM0_SIZE);
memcpy((uint8_t *)PRAM1_BASE, (uint8_t *)(FLASH_MAIN_BASE + PRAM0_SIZE),
PRAM1_SIZE);
memcpy((uint8_t *)PRAM2_BASE, (uint8_t *)(FLASH_MAIN_BASE + PRAM0_SIZE +
PRAM1_SIZE), PRAM2_SIZE);
memcpy((uint8_t *)PRAM3_BASE, (uint8_t *)(FLASH_MAIN_BASE + PRAM0_SIZE +
PRAM1_SIZE + PRAM2_SIZE),
PRAM3_SIZE);
SYSCTRL->FLASH_OVERLAY_CFG = 0xf;
/* Enable CM3 loop cache */
SYSCTRL->CSS_LOOP_CACHE_CFG = CSS_LOOP_CACHE_ENABLE;
/* Configure application-specific advertising data and scan response data*/
APP_SetAdvScanData();
/*配置服务*/
/* Configure Battery Service Server */
BASS_Setup();
/* Configure Device Information Service Server */
DISS_Setup();
/* Initialize Android Audio Streaming Hearing Aid service */
ASHA_Initialize(&ashaReadOnlyProperties, APP_ASHA_CallbackHandler);
/* Add application message handlers */
MsgHandler_Add(TASK_ID_GAPM, APP_GAPM_GATTM_Handler);
MsgHandler_Add(GATTM_ADD_SVC_RSP, APP_GAPM_GATTM_Handler);
MsgHandler_Add(TASK_ID_GAPC, APP_GAPC_Handler);
MsgHandler_Add(APP_LED_TIMEOUT, APP_LED_Timeout_Handler);
MsgHandler_Add(APP_BATT_LEVEL_LOW, APP_BASS_BattLevelLow_Handler);
/* Reset the GAP manager. Trigger GAPM_CMP_EVT / GAPM_RESET when finished.
* See SCAN_MsgHandler */
GAPM_ResetCmd();
}
#ifdef __cplusplus ///<end extern c
}
#endif
/******************************** End of file *********************************/
/**
* @file BLE_Port.h
*
* @date 2021/8/5
*
* @author aron566.
*
* @brief 蓝牙驱动
*
* @version v1.0
*/
#ifndef BLE_PORT_H
#define BLE_PORT_H
#ifdef __cplusplus ///<use C compiler
extern "C" {
#endif
/** Includes -----------------------------------------------------------------*/
#include <stdint.h> /*need definition of uint8_t*/
#include <stddef.h> /*need definition of NULL*/
#include <stdbool.h>/*need definition of BOOL*/
#include <stdio.h> /*if need printf*/
#include <stdlib.h>
#include <string.h>
#include <limits.h> /**< if need INT_MAX*/
/** Private includes ---------------------------------------------------------*/
#include <rwip_task.h>
/** Private defines ----------------------------------------------------------*/
/** Exported typedefines -----------------------------------------------------*/
/* APP Task messages */
enum appm_msg
{
APPM_DUMMY_MSG = TASK_FIRST_MSG(TASK_ID_APP),
APP_LED_TIMEOUT,
APP_BATT_LEVEL_LOW
};
/** Exported constants -------------------------------------------------------*/
/** Exported macros-----------------------------------------------------------*/
#define LED_DIO_NUM 6 /* DIO number that is connected to LED of EVB */
/* Timer setting in units of 10ms (kernel timer resolution) */
#define TIMER_SETTING_MS(MS) (MS / 10)
#define TIMER_SETTING_S(S) (S * 100)
/** Exported variables -------------------------------------------------------*/
/** Exported functions prototypes --------------------------------------------*/
/*蓝牙初始化*/
void BLE_Port_Init(void);
/*蓝牙接口启动*/
void BLE_Port_Start(void);
/*清空绑定列表*/
bool BLE_Port_BondList_RemoveAll(void);
#ifdef __cplusplus ///<end extern c
}
#endif
#endif
/******************************** End of file *********************************/
3、调用
int main(void)
{
/*时钟初始化*/
...
/*蓝牙接口初始化*/
BLE_Port_Init();
while(1)
{
/*启动蓝牙*/
BLE_Port_Start();
}
}
BLE Slave
BLE Master
BLE Audio Stream
基于RSL10的Bootloader
官方给了一个BL工程,我这边暂时没有使用,而是沿用之前编写的BL工程框架移植到RSL10中去,完美运行。代码约占用50KB(未开优化),官方8KB,所以功能方面差异较大。
特别需要注意的地方
调试相关
调试状态下,可以不设定看门狗和喂狗
退出调试一定开看门狗,否则设备不断重启,也就是看门狗无法关闭
功耗相关
int main(void)
{
while(1)
{
...
/* Wait for an event before executing the scheduler again. */
SYS_WAIT_FOR_EVENT;
}
}
电源
为了能够使用最小1.1 V的VBAT,应遵守以下降低的操作条件:
- 最大Tx功率0 dBm。
- SYSCLK≤24mhz。
- 功能温度范围限制在0−50℃
应采用以下微调参数:
- VCC = 1.10 v
- VDDC = 0.92 v
- VDDM = 1.05 V,将在电池寿命结束时受到VCC的限制
- VDDRF = 1.05 V,将在电池寿命结束时受到VCC的限制。
VDDPA应该关闭
当VCC低于1.03 V时,RSL10应进入end - of - battery - life工作模式。如果限制VBAT≥1.10 V, VCC将保持在1.03 V以上