前文介绍了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()
来实现定时器的开启与停止。
相应的,根据设置情况,需要在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接口介绍