ARM Cordio WSF(三)——WSF在nRF52840平台移植

这篇具有很好参考价值的文章主要介绍了ARM Cordio WSF(三)——WSF在nRF52840平台移植。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前文介绍了WSF架构及其WSF API,本文将介绍如何在一个硬件平台上使用WSF,这里基于stack项目下的nRF52840平台进行介绍。

3、PAL实现

对于要在一个处理器上运行WSF(逻辑情况),需要处理系统的调度、系统SysTick、中断上下文(Critical Section)、平台定时器相关,主要涉及内容如下:

  • timer移植,这里在平台相关的文件:pal_timer.c/h中实现;
  • 中断,Critical Section相关处理,避免中断执行上下文的破坏,平台相关文件:pal_sys.c/h;
  • 若需要使用NVM,则需要pal_flash.h/c
  • 对于wsf_heap相关操作,必要也需要在pal_sys.c/h中进行适配。

在WSF的架构中,均以PAL_*的形式来定义,即以平台抽象层(PAL,Platform Abstraction Layer)形式实现。

3.1 pal_sys

对于系统相关的PAL实现,关键接口如下(pal_sys.h):

/* Initialization */
void PalSysInit(void);

/* Diagnostics */
void PalSysAssertTrap(void);
void PalSysSetTrap(bool_t enable);
uint32_t PalSysGetAssertCount(void);
uint32_t PalSysGetStackUsage(void);

/* Power Management */
void PalSysSleep(void);  //休眠相关处理
bool_t PalSysIsBusy(void);
void PalSysSetBusy(void);
void PalSysSetIdle(void);

/* Critical Section */
void PalEnterCs(void);
void PalExitCs(void);

包括系统初始、诊断相关、功耗管理相关、以及Critical Section的处理。

/*! \brief      Free memory for pool buffers (align to word boundary). */
uint32_t palSysFreeMem[FREE_MEM_SIZE/sizeof(uint32_t)];

uint8_t *SystemHeapStart = (uint8_t *) palSysFreeMem;
uint32_t SystemHeapSize = FREE_MEM_SIZE;

关于Critical Section的处理,主要为中断的开启与关闭:

void PalEnterCs(void)
{
  #ifdef __IAR_SYSTEMS_ICC__
      __disable_interrupt();
  #endif
  #ifdef __GNUC__
      __asm volatile ("cpsid i");
  #endif
  #ifdef __CC_ARM
      __disable_irq();
  #endif
}
void PalSysInit(void)
{
  /* Enable Flash cache */
  NRF_NVMC->ICACHECNF |= (NVMC_ICACHECNF_CACHEEN_Enabled << NVMC_ICACHECNF_CACHEEN_Pos);

  /* Use 16 MHz crystal oscillator (system starts up using 16MHz RC oscillator). */
  NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
  NRF_CLOCK->TASKS_HFCLKSTART    = 1;
  while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) { }

  palSysAssertCount = 0;
  PalSysAssertTrapEnable = TRUE;
  palSysBusyCount = 0;

  PalRtcInit();
}

在平台初始化函数中,完成RTC初始化、系统相关设置,配置时钟等操作。

3.2 pal_timer

platform/pal_timer.c中,实现定时器驱动程序,用于调度程序和其他低功耗相关任务。其在实现时依赖于pal_rtc.c和一些调度程序以及nRF528**基带相关API。

对于一个调度定时器而言,通常需要实现以下相关接口(pal_timer.h):

/* Initialization */
void PalTimerInit(PalTimerCompCback_t expCback);
void PalTimerDeInit(void);

/* Control and Status */
PalTimerState_t PalTimerGetState(void);
void PalTimerStart(uint32_t expUsec);
void PalTimerStop(void);
uint32_t PalTimerGetCurrentTime(void);

以下为在NRF处理器上实现调度定时器,包括控制块、调度定时器初始化等。

/*! \brief      Scheduler timer driver control block. */
static struct
{
  PalTimerState_t    state;            /*!< State. */
  uint32_t           compareVal;       /*!<  Absolute compare value for timer expiry interrupt. */
  PalTimerCompCback_t expCback;         /*!< Timer expiry call back function. */
} palTimerCb;

调度定时器初始化:

void PalTimerInit(PalTimerCompCback_t expCback)
{
  #if SCH_TIMER_REQUIRED == TRUE
    #if BB_CLK_RATE_HZ == 32768
      PalRtcIrqRegister(RTC_CHANNEL_START_BB, palTimerRtcIrqHandler);
    #else
      /* Give scheduler timer the highest priority. */
      NVIC_SetPriority(TIMER1_IRQn, 0);  /* highest priority */
      NVIC_DisableIRQ(TIMER1_IRQn);

      /* stop timer if it was somehow running (timer must be stopped for configuration) */
      NRF_TIMER1->TASKS_STOP  = 1;

      /* clear timer to zero count */
      NRF_TIMER1->TASKS_CLEAR = 1;

      /* configure timer */
      NRF_TIMER1->MODE      = TIMER_MODE_MODE_Timer;
      NRF_TIMER1->BITMODE   = TIMER_BITMODE_BITMODE_32Bit;
      NRF_TIMER1->PRESCALER = PAL_TIMER_1MHZ_PRESCALER;  /* f = 16MHz / (2 ^ TIMER_PRESCALER) */

      /* timer1 is a free running clock. */
      NRF_TIMER1->TASKS_START = 1;

      /* Clear out and enable timer1 interrupt at system level. */
      NRF_TIMER1->INTENCLR = 0xFFFFFFFF;
      NRF_TIMER1->EVENTS_COMPARE[TIMER_CHANNEL_START_BB] = 0;
      NVIC_ClearPendingIRQ(TIMER1_IRQn);
      NVIC_EnableIRQ(TIMER1_IRQn);
    #endif
  #endif

  palTimerCb.compareVal = 0;
  palTimerCb.expCback = expCback;
  palTimerCb.state = PAL_TIMER_STATE_READY;
}

调度定时器的源根据宏定义,可以设置两种 :一种是RTC 32.768kHz的源;另一种是基于定时器Timer1来实现。

通过void PalTimerStart(uint32_t expTimeUsec)void PalTimerStop()来实现定时器的开启与停止。

相应的,根据设置情况,需要在palTimerRtcIrqHandlerTIMER1_IRQn中断中处理定时器相关。

void TIMER1_IRQHandler(void)
{
  /* Callback function could restart timer1. However, we blindly stop timer1 first. */
  NRF_TIMER1->INTENCLR = TIMER_INTENCLR_COMPARE0_Msk;
  /* Clear event again just in case. */
  NRF_TIMER1->EVENTS_COMPARE[TIMER_CHANNEL_START_BB] = 0;
  palTimerCb.state = PAL_TIMER_STATE_READY;
  if (palTimerCb.expCback)  //初始化中设定的定时器回调函数
  {
    palTimerCb.expCback();  
  }
}

3.3 pal_flash

对于平台上Flash相关操作,提供NVM的使用,通过pal_flash.h来实现。

/* Initialization */
void PalFlashInit(PalFlashCback_t actCback);
void PalFlashDeInit(void);

/* Control and Status */
PalFlashState_t PalNvmGetState(void);
uint32_t PalNvmGetTotalSize(void);
uint32_t PalNvmGetSectorSize(void);

/* Data Transfer */
void PalFlashRead(void *pBuf, uint32_t size, uint32_t srcAddr);
void PalFlashWrite(void *pBuf, uint32_t size, uint32_t dstAddr);
void PalFlashEraseSector(uint32_t size, uint32_t startAddr);
void PalFlashEraseChip(void);

以nRF52840为例,为QSPI Flash,其实现上,通过完成对底层驱动的初始化,进而可实现对Flash的访问。

void PalFlashInit(PalFlashCback_t actCback)
{
  uint32_t status;
  uint8_t  temp = 0x40;

  (void)actCback;

  nrfx_qspi_config_t config =
  {                                                                       \
      .xip_offset  = NRFX_QSPI_CONFIG_XIP_OFFSET,                         \
      .pins = {                                                           \
         .sck_pin     = BSP_QSPI_SCK_PIN,                                 \
         .csn_pin     = BSP_QSPI_CSN_PIN,                                 \
         .io0_pin     = BSP_QSPI_IO0_PIN,                                 \
         .io1_pin     = BSP_QSPI_IO1_PIN,                                 \
         .io2_pin     = BSP_QSPI_IO2_PIN,                                 \
         .io3_pin     = BSP_QSPI_IO3_PIN,                                 \
      },                                                                  \
      .irq_priority   = (uint8_t)NRFX_QSPI_CONFIG_IRQ_PRIORITY,           \
      .prot_if = {                                                        \
          .readoc     = (nrf_qspi_readoc_t)NRFX_QSPI_CONFIG_READOC,       \
          .writeoc    = (nrf_qspi_writeoc_t)NRFX_QSPI_CONFIG_WRITEOC,     \
          .addrmode   = (nrf_qspi_addrmode_t)NRFX_QSPI_CONFIG_ADDRMODE,   \
          .dpmconfig  = false,                                            \
      },                                                                  \
      .phy_if = {                                                         \
          .sck_freq   = (nrf_qspi_frequency_t)NRFX_QSPI_CONFIG_FREQUENCY, \
          .sck_delay  = (uint8_t)NRFX_QSPI_CONFIG_SCK_DELAY,              \
          .spi_mode   = (nrf_qspi_spi_mode_t)NRFX_QSPI_CONFIG_MODE,       \
          .dpmen      = false                                             \
      },                                                                  \
  }
  ;

  /* Verify palFlashCacheBuf size is at least 2. */
  PAL_FLASH_PARAM_CHECK(PAL_FLASH_CACHE_BUF_SIZE >= 2);

  status = nrfx_qspi_init(&config, NULL, NULL);

  PAL_FLASH_PARAM_CHECK(status == NRFX_SUCCESS);

  nrf_qspi_cinstr_conf_t cinstr_cfg = {
      .opcode    = QSPI_STD_CMD_RSTEN,
      .length    = NRF_QSPI_CINSTR_LEN_1B,
      .io2_level = 1,
      .io3_level = 1,
      .wipwait   = 1,
      .wren      = 1
  };

  /* Send reset enable. */
  status = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);

  /* Send reset command */
  cinstr_cfg.opcode = QSPI_STD_CMD_RST;

  status = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);

  PAL_FLASH_PARAM_CHECK(status == NRFX_SUCCESS);

  /* Switch to qspi mode */
  cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
  cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;

  status = nrfx_qspi_cinstr_xfer(&cinstr_cfg, &temp, NULL);

  PAL_FLASH_PARAM_CHECK(status == NRFX_SUCCESS);

  memset(&palFlashCb, 0, sizeof(palFlashCb));

  palFlashCb.state = PAL_FLASH_STATE_READY;

  (void)status;

}

对于Flash的读与写实现:

void PalFlashRead(void *pBuf, uint32_t size, uint32_t srcAddr)
{
  uint32_t readSize = PAL_FLASH_WORD_ALIGN(size);
  uint32_t actualSize = size;
  uint32_t status;
  uint16_t addrOffset = 0;

  do
  {
    if (readSize <= sizeof(palFlashCacheBuf))
    {
      /* Read data. */
      status = nrfx_qspi_read(palFlashCacheBuf, readSize, srcAddr + addrOffset);
      memcpy((uint8_t*)pBuf + addrOffset, palFlashCacheBuf, actualSize);

      readSize = 0;
    }
    else
    {
      /* Read data. */
      status = nrfx_qspi_read(palFlashCacheBuf, sizeof(palFlashCacheBuf), srcAddr + addrOffset);

      memcpy((uint8_t*)pBuf + addrOffset, palFlashCacheBuf, sizeof(palFlashCacheBuf));

      addrOffset += sizeof(palFlashCacheBuf);
      readSize -= sizeof(palFlashCacheBuf);
      actualSize -= sizeof(palFlashCacheBuf);
    }
  } while (readSize != 0);
  (void)status;
}
void PalFlashWrite(void *pBuf, uint32_t size, uint32_t dstAddr)
{
  uint32_t writeSize = PAL_FLASH_WORD_ALIGN(size);
  uint32_t actualSize = size;
  uint32_t status;
  uint16_t addrOffset = 0;

  do
  {
    if (writeSize <= sizeof(palFlashCacheBuf))
    {
      memcpy(palFlashCacheBuf, (uint8_t*)pBuf + addrOffset, actualSize);
      memset((uint8_t*)palFlashCacheBuf + actualSize, 0xFF, sizeof(palFlashCacheBuf) - actualSize);

      /* Write data. */
      status = nrfx_qspi_write(palFlashCacheBuf, writeSize, dstAddr + addrOffset);

      writeSize = 0;
    }
    else
    {
      memcpy(palFlashCacheBuf, (uint8_t*)pBuf + addrOffset, sizeof(palFlashCacheBuf));

      /* Write data. */
      status = nrfx_qspi_write(palFlashCacheBuf, sizeof(palFlashCacheBuf), dstAddr + addrOffset);

      addrOffset += sizeof(palFlashCacheBuf);
      writeSize -= sizeof(palFlashCacheBuf);
      actualSize -= sizeof(palFlashCacheBuf);
    }
  } while (writeSize != 0);
  (void)status;
}

3.4 SysTick实现

对于Cortex-M系列的处理器,都可以使用SysTick_Handler来实现系统的tick,这样更具有移植性。
对于SysTick_Handler的处理,
1)关键管理一个系统的tick,这里的示例使用32bit全局变量来实现;
2)调用WSF中的Timer来更新Tick,对于设置的wsf_timer,超时的,将触发定时器任务,进而触发相应的事件。

//由于在中断上下文处理,使用volatile关键字
volatile uint32_t g_sys_tick_count = 0;

void SysTick_Handler(void)  
{
    g_sys_tick_count++;
    WsfTimerUpdateTicks();
}

关于SysTick时钟的设备,则,根据系统期望的tick情况(如10ms)来设置。

    /* Configure SysTick to generate an interrupt every millisecond */
    SysTick_Config(WSF_MS_PER_TICK * GetSystemCoreClock() / 1000);     

其中,GetSystemCoreClock获取系统时钟情况,不同平台根据设置的系统时钟来设置。

基本调用逻辑与思路如下图所示:
ARM Cordio WSF(三)——WSF在nRF52840平台移植,操作系统,蓝牙技术,arm开发,智能硬件,系统架构
WsfTimerUpdate中,将已经超时的Task设置定时器事件。

/* timer expired; set task for this timer as ready */
WsfTaskSetReady(pElem->handlerId, WSF_TIMER_EVENT);

void WsfTaskSetReady(wsfHandlerId_t handlerId, wsfTaskEvent_t event)
{
    /* Unused parameter */
    (void)handlerId;

    WSF_CS_INIT(cs);

    uint32_t lock = WSF_CS_ENTER();
    wsfOs.task.taskEventMask |= event;
    WSF_CS_EXIT(lock);

    /* set event in OS */
}

最终,在wsfOsDispatcher中调用。

    /*--- Start OS Dispatcher in a loop ---*/
    while(1)
    {
        wsfOsDispatcher();
    }

持续更新,系列文章,收藏关注吧!

1、ARM Cordio WSF(一)——架构介绍
2、ARM Cordio WSF(二)——API接口介绍文章来源地址https://www.toymoban.com/news/detail-855863.html

到了这里,关于ARM Cordio WSF(三)——WSF在nRF52840平台移植的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于nRF52840 Dongle配合Wireshark对Mesh网络抓包并解析(Nordic)

    1)nRF52840 Dongle 2)Mesh节点 3)手机作为配网器(苹果手机安装nRF Mesh APP) 1)节点使用的例程目录:nrf5sdkformeshv500srcexampleslight_switchclient 2)抓包使用的软件:Wireshark 3)抓包方法:使用Nordic的nRF52840 Dongle配合Wireshark对蓝牙设备抓包(BLE) 先打开Wireshark,这时候能收到节点的

    2024年02月10日
    浏览(42)
  • Linux操作系统下Docker和Docker Compose的安装教程(包含x86和arm64平台离线一键安装资源包)

    本文章将详细介绍Linux下Docker和Docker Compose的安装教程。 目录3为x86和arm64平台Docker离线安装资源包,包含Docker Compose、一键安装脚本使用教程。 相关文章参考: Docker常用基础命令 Docker批量清理删除镜像和容器常用命令 版本如下 名称 版本 CentOS 7.6+ openEuler等其他操作系统可以使

    2024年02月06日
    浏览(50)
  • LWIP——无操作系统移植

    目录 移植说明 LwIP前期准备  以太网DMA描述符 LwIP移植流程 添加网卡驱动程序 添加LwIP源文件  移植头文件 网卡驱动编写 移植总结  LwIP的移植可以分为两大类:第一类是只移植内核核心,此时用户应用程序编写只能基于RaW/CallBack API进行;第二类是移植内核核心和上层API函数

    2024年02月12日
    浏览(45)
  • LwIP带操作系统的移植

    目录 LwIP移植前期准备 LwIP移植流程 修改lwipopts.h 修改lwip_comm.c文件 修改ethernetif.c/h文件 修改ethernetif_input函数 修改ethernet.c文件 添加应用程序 LwIP是支持操作系统的,在操作系统的支持下我们可以使用LwIP提供的另外两种API编程接口编程。没有操作系统的时候,我们只能使用R

    2024年02月07日
    浏览(41)
  • STM32FreeRTOS操作系统移植

    移植好的FreeRTOS模板: 链接:https://pan.baidu.com/s/1_87VQAWXUl4jTqSCZ0MFjw?pwd=dw52  提取码:dw52 1.在工程中新建FreeRTOS文件夹 2.把源码source里面的文件全部粘贴进FreeRTOS文件夹中  3.在portable文件中只保留一下文件,其余删除  4.新建如下两个文件夹  5.在FreeRTOS_CODE目录下添加以下文件

    2024年02月08日
    浏览(46)
  • STM32移植FreeRTOS操作系统

    一、FreeRTOS源码下载 (1)移植钱得准备前菜对吧,我们先来去官网瞄一瞄 网址:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions 第一步:点击下载FreeRTOS 第二步:选择版本下载(我选择稳定版本) 注:我们下载的稳定版本不包含DEMO例

    2024年01月17日
    浏览(41)
  • 基于STM32的实时操作系统FreeRTOS移植教程(手动移植)_stm32移植freertos(1)

    直接意识代码: 这是我们大脑最希望的添加代码方式,很显然他是 错的 , 两个任务之间产生了相互的影响 ,使得两个任务都执行错误,这种思想在 裸机开发 中肯定是 错的 ,但是在我们的 RTOS 中他就可以是 对的 。 任务型代码: 这是 独立的两个任务内容 ,我们只需要把

    2024年04月10日
    浏览(109)
  • 【STM32】STM32 移植鸿蒙操作系统

    随着 OpenHarmony3.1 的正式发布,其功能也在不断完善。OpenHarmony LiteOS-M 内核是面向IoT领域构建的轻量级物联网操作系统内核,具有小体积、低功耗、高性能的特点,其代码结构简单,主要包括内核最小功能集、内核抽象层、可选组件以及工程目录等,分为硬件相关层以及硬件无

    2024年02月09日
    浏览(54)
  • MQTT协议版本Mosquitto移植到嵌入式arm平台

    MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智

    2024年02月06日
    浏览(55)
  • QT·移植Qt到ARM平台及搭建Qt交叉编译环境

    目录 一、编译tslib库 二、移植 tslib 到文件系统  三、编译Qt源码(用于移植到ARM) 四、移植Qt到文件系统  五、搭建 Qt Creator 交叉编译环境  六、获得Qt可执行文件的另一种方法 要想在ARM平台上运行Qt程序,得满足以下两个点: 1、ARM平台上搭建Qt运行环境 2、获得通过交叉编

    2024年02月02日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包