天天看點

RVB2601應用開發實戰系列一: Helloworld最小系統ifdef CONFIG_XIPendifif defined(CONFIG_KERNEL_RHINO)elif defined(CONFIG_KERNEL_FREERTOS)elif defined(CONFIG_KERNEL_UCOS)endififdef CONFIG_OS_TRACEendif

  1. 引言

RVB2601開發闆是基于CH2601晶片設計的生态開發闆,其具有豐富的外設功能和聯網功能,可以開發設計出很多有趣的應用。為了開發者更好的了解如何在CH2601上開發應用,本文介紹了如何移植對接CH2601晶片到YoC最小系統,開發第一個我的helloworld程式。

整個開發移植工作,我們都全部基于劍池CDK內建開發環境進行開發。劍池CDK以極簡開發為理念,是專業為IoT應用開發打造的內建開發環境。它在不改變使用者開發習慣的基礎上,全面接入雲端開發資源,結合 圖形化的OSTracer、Profiling等調試分析工具,加速使用者産品開發。想要了解更多劍池CDK開發資訊,請前往平頭哥晶片開發社群裡內建開發環境擷取更多。

建議在在看本文之前,先詳細看下RVB2601開發闆快速上手教程。本例程名為ch2601_helloworld_demo,可以通過CDK直接從OCC拉取。

  1. 最小系統移植适配

YoC最小系統包括對AliOS Things核心的移植,涉及到任務切換時的處理器上下文儲存和恢複 ,中斷事件處理,時鐘心跳初始化等。利用一個任務不斷周期性的列印"Helloworld"來示範最小系統移植成功。

2.1 适配YoC 核心

進入ch2601_helloworld目錄,打開工程檔案,所有的元件代碼都位于packages節點下, 點選packages下的 rhino_arch 包。該元件包含了ARM、CSKY、RISCV等架構下的任務排程的代碼,假如架構相同,則直接使用包内代碼,若不存在,需要按照接口,将port_s.S、port_c.c等代碼實作。具體目錄結構如下圖:

RVB2601應用開發實戰系列一: Helloworld最小系統ifdef CONFIG_XIPendifif defined(CONFIG_KERNEL_RHINO)elif defined(CONFIG_KERNEL_FREERTOS)elif defined(CONFIG_KERNEL_UCOS)endififdef CONFIG_OS_TRACEendif

由于CH2601使用了RISC-V 32bit處理器, 我們使用rv32_32gpr的具體實作,根據Kernel的對接分為以下幾個部分

2.1.1 任務切換相關

cpu_intrpt_switch

該功能函數定義在rhino_arch/src/riscv/rv32_32gpr/port_c.S裡, 主要使用者觸發軟中斷,切換任務。使用者可以通過該接口來實作任務切換。

cpu_intrpt_switch:

li      t0, 0xE080100C
lb      t1, (t0)
li      t2, 0x01
or      t1, t1, t2
sb      t1, (t0)
ret

tspend_handler           

該功能函數定義在rhino_arch/src/riscv/rv32_32gpr/port_c.S裡, 作為tspend中斷的處理函數接口,主要用于儲存目前的任務上下文,切換将要運作的下一個任務後,恢複下一個任務上下文。

tspend_handler:

addi    sp, sp, -124
sw      x1, 0(sp)
sw      x3, 4(sp)
sw      x4, 8(sp)
sw      x5, 12(sp)
sw      x6, 16(sp)
sw      x7, 20(sp)
sw      x8, 24(sp)
sw      x9, 28(sp)
sw      x10, 32(sp)
sw      x11, 36(sp)
sw      x12, 40(sp)
sw      x13, 44(sp)
sw      x14, 48(sp)
sw      x15, 52(sp)
sw      x16, 56(sp)
sw      x17, 60(sp)
sw      x18, 64(sp)
sw      x19, 68(sp)
sw      x20, 72(sp)
sw      x21, 76(sp)
sw      x22, 80(sp)
sw      x23, 84(sp)
sw      x24, 88(sp)
sw      x25, 92(sp)
sw      x26, 96(sp)
sw      x27, 100(sp)
sw      x28, 104(sp)
sw      x29, 108(sp)
sw      x30, 112(sp)
sw      x31, 116(sp)
csrr    t0, mepc
sw      t0, 120(sp)
la      a1, g_active_task
lw      a1, (a1)
sw      sp, (a1)
li      t0, 0xE000E100
lw      t1, (t0)
li      t2, 0xFEFFFFFF
and     t1, t1, t2
sw      t1, (t0)           

__task_switch_nosave:

la      a0, g_preferred_ready_task
la      a1, g_active_task
lw      a2, (a0)
sw      a2, (a1)
lw      sp, (a2)
/* Run in machine mode */
li      t0, MSTATUS_PRV1
csrs    mstatus, t0
lw      t0, 120(sp)
csrw    mepc, t0
lw      x1, 0(sp)
lw      x3, 4(sp)
lw      x4, 8(sp)
lw      x5, 12(sp)
lw      x6, 16(sp)
lw      x7, 20(sp)
lw      x8, 24(sp)
lw      x9, 28(sp)
lw      x10, 32(sp)
lw      x11, 36(sp)
lw      x12, 40(sp)
lw      x13, 44(sp)
lw      x14, 48(sp)
lw      x15, 52(sp)
lw      x16, 56(sp)
lw      x17, 60(sp)
lw      x18, 64(sp)
lw      x19, 68(sp)
lw      x20, 72(sp)
lw      x21, 76(sp)
lw      x22, 80(sp)
lw      x23, 84(sp)
lw      x24, 88(sp)
lw      x25, 92(sp)
lw      x26, 96(sp)
lw      x27, 100(sp)
lw      x28, 104(sp)
lw      x29, 108(sp)
lw      x30, 112(sp)
lw      x31, 116(sp)
addi    sp, sp, 124
mret

2.1.2 第一個任務初始化           

cpu_first_task_start

該功能函數定義在rhino_arch/src/riscv/rv32_32gpr/port_c.S裡, 作為第一個任務啟動接口。使用者通過調用該接口來實作第一個任務的啟動。

cpu_first_task_start:

j       __task_switch_nosave

cpu_task_stack_init           

該功能函數定義在rhino_arch/src/riscv/rv32_32gpr/port_c.c裡, 用于初始化第一個任務的的上下文,使用者可以通過調用該接口來實作第一個任務的執行入口,輸入參數等。

void cpu_task_stack_init(cpu_stack_t stack_base, size_t stack_size,

void *arg, task_entry_t entry)           

{

cpu_stack_t *stk;
register int *gp asm("x3");
uint32_t temp = (uint32_t)(stack_base + stack_size);
temp &= 0xFFFFFFF8UL;
stk = (cpu_stack_t *)temp;
*(--stk) = (uint32_t)entry;                   /* PC            */
*(--stk) = (uint32_t)0x31313131L;             /* X31           */
*(--stk) = (uint32_t)0x30303030L;             /* X30           */
*(--stk) = (uint32_t)0x29292929L;             /* X29           */
*(--stk) = (uint32_t)0x28282828L;             /* X28           */
*(--stk) = (uint32_t)0x27272727L;             /* X27           */
*(--stk) = (uint32_t)0x26262626L;             /* X26           */
*(--stk) = (uint32_t)0x25252525L;             /* X25           */
*(--stk) = (uint32_t)0x24242424L;             /* X24           */
*(--stk) = (uint32_t)0x23232323L;             /* X23           */
*(--stk) = (uint32_t)0x22222222L;             /* X22           */
*(--stk) = (uint32_t)0x21212121L;             /* X21           */
*(--stk) = (uint32_t)0x20202020L;             /* X20           */
*(--stk) = (uint32_t)0x19191919L;             /* X19           */
*(--stk) = (uint32_t)0x18181818L;             /* X18           */
*(--stk) = (uint32_t)0x17171717L;             /* X17           */
*(--stk) = (uint32_t)0x16161616L;             /* X16           */
*(--stk) = (uint32_t)0x15151515L;             /* X15           */
*(--stk) = (uint32_t)0x14141414L;             /* X14           */
*(--stk) = (uint32_t)0x13131313L;             /* X13           */
*(--stk) = (uint32_t)0x12121212L;             /* X12           */
*(--stk) = (uint32_t)0x11111111L;             /* X11           */
*(--stk) = (uint32_t)arg;                     /* X10           */
*(--stk) = (uint32_t)0x09090909L;             /* X9            */
*(--stk) = (uint32_t)0x08080808L;             /* X8            */
*(--stk) = (uint32_t)0x07070707L;             /* X7            */
*(--stk) = (uint32_t)0x06060606L;             /* X6            */
*(--stk) = (uint32_t)0x05050505L;             /* X5            */
*(--stk) = (uint32_t)0x04040404L;             /* X4            */
*(--stk) = (uint32_t)gp;                      /* X3            */
*(--stk) = (uint32_t)krhino_task_deathbed;    /* X1            */
return stk;           

}

2.1.3 核心心跳時鐘初始化

核心心跳時鐘主要用于系統時鐘的計時,系統任務的切換等。我們可以采用一個普通的定時器來做為系統心跳時鐘。

SystemInit

該功能函數定義在chip_ch2601/sys/system.c, 實作對整個系統的進行初始化,包括對系統核心時鐘,CACHE初始化等。

void SystemInit(void)

enable_theadisaee();
cache_init();
section_init();
interrupt_init();
soc_set_sys_freq(CPU_196_608MHZ);
csi_etb_init();
sys_dma_init();
csi_tick_init();           

ifdef CONFIG_XIP

sys_spiflash_init();           

endif

bootrom_uart_uninit();           

csi_tick_init

該功能函數在chip_ch2601/sys/tick.c,實作核心心跳的初始化,通過回調函數tick_event_cb 對系統時鐘進行技術,同時通過調用krhino_tick_proc實作對系統任務的排程。

csi_error_t csi_tick_init(void)

csi_error_t ret;
csi_tick = 0U;
ret = csi_timer_init(&tick_timer, CONFIG_TICK_TIMER_IDX);
if (ret == CSI_OK) {
    ret = csi_timer_attach_callback(&tick_timer, tick_event_cb, NULL);
    if (ret == CSI_OK) {
        ret = csi_timer_start(&tick_timer, (1000000U / CONFIG_SYSTICK_HZ));
    }
}
return ret;           

void csi_tick_increase(void)

csi_tick++;           

static void tick_event_cb(csi_timer_t timer_handle, void arg)

csi_tick_increase();           

if defined(CONFIG_KERNEL_RHINO)

krhino_tick_proc();           

elif defined(CONFIG_KERNEL_FREERTOS)

xPortSysTickHandler();           

elif defined(CONFIG_KERNEL_UCOS)

OSTimeTick();           

2.1.4 核心初始化

在任務啟動前,需要對核心做初始化,最後調用aos_start來啟動第一個任務。

aos_init

該功能函數位于aos/src/main.c, 用于初始化核心,啟動第一個任務。

int pre_main(void)

/* kernel init */
aos_init();           

ifdef CONFIG_OS_TRACE

trace_init_data();           

/* init task */
aos_task_new_ext(&app_task_handle, "app_task", application_task_entry,
                 NULL, INIT_TASK_STACK_SIZE, AOS_DEFAULT_APP_PRI);
/* kernel start */
aos_start();
return 0;           

aos_start

該功能函數用于啟動核心,運作第一個任務。

至此,YoC核心部分适配結束,編譯通過後就可以進行Helloworld應用程式開發了。

2.2 開發helloworld程式

2.2.1 序列槽初始化

在app/src/init/init.c裡完成board初始化函數裡完成序列槽的初始化。

void board_yoc_init()

board_init();
// uart_csky_register(CONSOLE_UART_IDX);
console_init(CONSOLE_UART_IDX, 115200, 128);
ulog_init();
aos_set_log_level(AOS_LL_DEBUG);

LOGI(TAG, "Build:%s,%s",__DATE__, __TIME__);
board_cli_init();           

console_init

該功能函數用于序列槽的初始化。

ulog_init

該功能函數用于列印功能的初始化。

2.2.2 列印Helloworld

最後在main函數裡實作helloworld的循環列印。

int main(void)

board_yoc_init();
LOGD(TAG, "%s\n", aos_get_app_version());
while (1) {
    LOGD(TAG, "Hello world! YoC");
    sample_test();
    aos_msleep(1000);
}
return 0;           

2.3. 編譯運作

編譯通過後,下載下傳到RVB2601開發闆後複位運作(具體下載下傳運作操作可以參考RVB2601開發闆快速上手教程),看到序列槽視窗出現一下列印,說明移植成功。

RVB2601應用開發實戰系列一: Helloworld最小系統ifdef CONFIG_XIPendifif defined(CONFIG_KERNEL_RHINO)elif defined(CONFIG_KERNEL_FREERTOS)elif defined(CONFIG_KERNEL_UCOS)endififdef CONFIG_OS_TRACEendif
  1. 總結

RVB2601最小系統hellworld主要實作對YoC系統的核心适配,具備RTOS的基本能力,實作簡單的序列槽列印。後續還有更精彩的實戰案例,敬請期待。

繼續閱讀