前文介绍了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 == 32768PalRtcIrqRegister(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#endifpalTimerCb.compareVal = 0;palTimerCb.expCback = expCback;palTimerCb.state = PAL_TIMER_STATE_READY;
}
调度定时器的源根据宏定义,可以设置两种 :一种是RTC 32.768kHz的源;另一种是基于定时器Timer1来实现。
通过void PalTimerStart(uint32_t expTimeUsec)
与void PalTimerStop()
来实现定时器的开启与停止。
相应的,根据设置情况,需要在palTimerRtcIrqHandler
或TIMER1_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
获取系统时钟情况,不同平台根据设置的系统时钟来设置。
基本调用逻辑与思路如下图所示:
在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接口介绍