本帖最后由 jszszzy 于 2021-11-20 09:52 编辑
安信可PB-02模组评测(2)---PHY62XX 架构介绍 && 设计任务介绍
本篇文章于EEWORLD首发,详情移步:EEWORLD评测
PHY6212 芯片介绍
安信可科技PB-02是一款基于PHY6212芯片研发设计的BLE5.0蓝牙模块。
PHY6212 是一款超低功耗物联网蓝牙无线通信芯片,搭载 32 位ARM®Cortex™-M0 CPU,配备138KSRAM/Retention SRAM,具有超低功耗、高性能和无线多模的特点,支持安全性、应用和无线更新的 BLE 功能。
具体参数和功能参看图1
图1: PHY6212详情页
总的来说是一款功能齐全的无线mcu。
PHY6212 的存储
PHY6212 共有 128KB 的 ROM, 138KB 的 SRAM 和高达 512KB 闪存(手册说)。这些内存的物理地址空间如图所示。
图2: PHY6212的存储空间
PHY6212 引导装载程序介绍
PHY62XX 的固件架构分为三部分,ROM 部分,OTA Bootloader,应用固件,其中 OTA Bootloader 为可选过程,下图为两种启动过程流程。
支持 OTA 模式启动:
图3: PHY6212 OTA 模式
非 OTA 模式启动:
图4: PHY6212非 OTA 模式
- ROM 部分: 启动并引导-或者 Boot loader 或者 Application 和 BLE 协议栈 API,启动通过TM
管脚的高低电平选择编程模式(高电平)还是正常启动模式(低电平)。
- OTA Boot loader: 用于引导 Application 以及处理 OTA 升级。
- Application: 应用代码,绝大部分二次开发工作都集中在 Application 部分。
引导期间,ROM1 将转化为 0x0 地址,M0 将从 ROM1 开始执行程序。
图5: PHY6212 BOOT启动
ROM 中的引导加载程序的基本结构如下所示。FLASH 中的内容需进行特别定义,以保证引导加载程序能够识别 FLASH 内容是否有效,如下面的案例所示。如果 FLASH 是有效的,ROM 引导加载程序将以正常模式启动芯片,并开始执行程序;如果 FLASH 无效,引导加载程序将进入 FLASH编程模式。
图6: PHY6212 BOOT启动流程图
值得注意的是,向FLASH写入的程序是从ROM读取后进入镜像模式在RAM种运行代码写入的
PHY6212 时钟结构介绍
两个晶体时钟源:16MHz 晶体振荡器(XT16M)和 32.768kHz 晶体振荡器(XT32k),其中 32.768k 晶体振荡器处于备用状态。芯片上的 RC 振荡器也有两个:32MHz RC 振荡器(RC32M)和 32kHz RC振荡器(RC32k),二者均可根据 16MHz 晶体振荡器进行校准。若未安装 32.768kHz 晶体振荡器,RTC 将根据 RC32k 晶体振荡器进行周期性校准。若处于 XT16M 晶体振荡器启动前的初始或唤醒状态,RC32M 将作为主时钟使用。芯片上的 DLL 可以根据 XT16M 时钟源生成频率更高的时钟,如32/48/64/96MHz。
值得注意的:
PCRM 就是控制着各种外设的主时钟。
图7: PHY6212时钟框图
PHY6212 中断介绍
这部分手册介绍的少之又少,只能从代码里面的向量表摘出来让大家看一看,如图是其所有可自定义的外部中断涉及到了所有外设。具体到使用时我们在具体介绍外设所对应的中断。
PHY6212 特殊外设介绍(具体内容在项目使用时会详细介绍)
- 随机数发生器(RNG)
随机数发生器(RNG)根据内部热噪声产生真实的非确定性随机数,这些随机数可用于加密目的。
RNG 不需要种子值。
- IOMUX
IOMUX 提供了一种灵活的 I/O 配置,因为大多数外围设备的端口都可以配置并映射到任何物理I/O 触点(甚至是模具边界)上。这些外围模块包括 I2C 0-1、I2S、UART、PWM 0-5、SPI 0-1、正交解码器等。然而,对于其他特定用的外设,它们的 IOs 映射在启用时是固定的,这些专用外设包括 JTAG、analog_ios、GPIOs 和密钥扫描。
- KSCAN
键扫描最高支持 16 行 18 列的键矩阵,每个行或列均可通过寄存器单独设置启用或禁用,也可以配置 GPIO 引脚用于键扫描。某些键扫描的参数可以通过寄存器设置,包括极性(按键力度)、单按键或多按键、255us 步进的从 0ms 到 128ms 抖动滤波时间(有效按键时内)。当键扫描可以执行中断时,有效的按键操作即可触发中断。中断完成后,在中断状态寄存器位中输入“1”即可退出中断状态。键扫描支持手动模式和自动模式。手动模式下,键扫描收到中断信号,单片机/软件将检查输入引脚并扫描输出引脚,以确定哪些键被按下。手动模式相对较慢,且需要进行 CPU 处理。与之相对,自动模式下,键扫描将自动检查输入和输出引脚,同时将与按下的键对应的行/列信息存储到只读寄存器中,随即触发软件中断并检索按键信息。
- 正交解码器
正交解码器可进行带输入脱扣滤波器的四倍频编码传感信号缓冲解码,适用于机械和光学传感器。可配置示例周期和累积,以匹配应用程序需求。正交解码器支持三轴功能和索引通道,可编程为 4x/2x/1x 计数模式。
PHY6212 SDK软件架构介绍(详细代码分析见注释)
终于到了介绍SDK开发的部分,经过前面的介绍相信各位对本MCU的启动流程、架构结构、特殊外设有了一部分了解,下面就开始介绍如何基于本SDK开发。
我为什么拖了这么久才开始呢?,一部分是不希望盲目开发,PHY6212 的开发并不是纯粹的裸机开发,我们应该用操作系统的概念去理解其SDK的强大,另一部分原因还是因为安信可提供对于SDK的开发手册对于重要部分并不完全,需要深入看代码来理解,尤其是对于OSAL这一部分介绍少之又少,如图7可以说就是一句话了事,这就导致开发者很尴尬。
图8: OSAL手册介绍
好了吐槽完了我们回到正题,什么是OSAL?为什么我们要基于OSAL来进行开发?让我们再来看一张图。
图9: 系统框架图
OSAL就是本SDK的抽象操作系统层,可以看到关于BLE的所有内容都是基于OSAL的Task来进行运行的,如果要进行蓝牙通信,通过TASK的同步机制来进行通信是无可避免的,所以从一开始就必须基于操作系统的思想来进行开发。那么如何开发呢?我们以最简单的GPIO历程为例子来介绍OSAL是如何启动的和 存在的问题。
OSAL和系统外设的初始化都在main.c中进行,在项目如图位置。
这里详细分析一下main.c是如何将系统开启的,和如何进行的初始化。
int main(void)
{
// init global configuration of SOC
int rrn = __return_address();//汇编函数,具体返回地址:SRAM0 0X1FFF4959
g_system_clk = SYS_CLK_DLL_48M;
osal_mem_set_heap((osalMemHdr_t *)g_largeHeap, LARGE_HEAP_SIZE);//设置系统堆栈
init_config();//API并没有开放,推测是针对系统的初始化?
hal_pwrmgr_init();//休眠控制初始化
hal_rfphy_init();// 射频初始化
hal_init();//外设初始化
LOG("%x\n", rrn);//打印出此地址
app_main(); //开应用主函数
}
这里需要提示一下,LOG这个提示符可能会报错主要原因在于宏定义书写存在问题。
我将下图的报错位置修改如下
将此处修改成:报错消失。
大部分外设都在main.c的 hal_init() 完成初始化,用户添加外设初始化时应将所需外设初始化放到此处。值得一提的时调用hal_gpio_init();
是也同时完成了对应外设的中断优先级、回调函数的设置。
hal_init()
的内容如下:
void hal_init(void)
{
hal_system_init(g_system_clk);//主时钟选择
hal_pwrmgr_RAM_retention(RET_SRAM0|RET_SRAM1|RET_SRAM2|RET_SRAM3|RET_SRAM4);
//配置休眠过程中能够保持的 RAM,Bit 0~4 有效,分别对应 RAM0~RAM4,如果对应的 Bit为 1,这块 RAM 在休眠过程中数据能够保持,如果为 0,那么休眠过程中,数据会丢失
hal_rtc_clock_config(CLK_32K_XTAL);//CLK_32K_RCOSC);//32K时钟选择
LOG_INIT();//调试LOG初始化
hal_gpio_init();//GPIO初始化
hal_adc_init();//ADC初始化
LOG( "all driver init OK!\n" );
}
到了这一步只是将OSAL的准备工作完成了,app_main() 的开启就是OSAL的开启。我不建议在此处写上任何类似裸机的代码这是不对的,此处主要目的还是开启OSAL的各个任务,若果在此处写就是浪费了OSAL消耗的性能。很遗憾的是这些代码都没有开放所以我们只能暂且知道,这里是OSAL的系统开始,其中 osal_start_system();
函数会以循环的形式执行已经向系统注册的任务。
int app_main(void)
{
/* Initialize the operating system */
osal_init_system();
osal_pwrmgr_device( PWRMGR_BATTERY );
/* Start OSAL */
osal_start_system(); // No Return from here
return 0;
}
那么我们如何创建自己的任务到系统中呢,以我上文为例子,在系统的跳转表中找到用户自定义任务初始化的入口,但是这里有一个疑问,为什么注释说这些函数会被ROM所调用?系统的跳转表是如何具体运行的?手册这里也没有指明。
用户应用的真实入口–>osalInitTasks
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
//为所有task申请内存空间
LL_Init( taskID++);//Link layerc层初始化
/*
Application
*/
Led_Demo_Init();
}
值得注意的是,需要先将task手动写入其注册表且顺序必须相同:
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent,
};
到此系统层面的启动就彻底完成了,可以愉快的进行应用开发了。
PHY6212 设计目标
现在有两个想法:
1. 基于低功耗方面设计一个可以进行蓝牙通信的空调省电助手
2. 基于kscan设计小号蓝牙键盘
具体内容会随着外设使用效果再说。
PHY6212 SDK软件架构遗留的问题
请安信可专家看一下这里!问题汇总:
- :PHY6212 中断表:
此图中这几个中断是干什么的,手册没有详细解释。
- jump_table的运行机制是什么?为什么注释说这些函数会被ROM所调用?系统的跳转表是如何具体运行的?
- main.c代码中 rrn地址是什么的地址?
int rrn = __return_address();//汇编函数,具体返回地址:SRAM0 0X1FFF4959