天天看點

RVB2601應用開發實戰系列一: Helloworld最小系統

關于RVB2601,我們已經為大家推送了 RISC-V應用創新大賽 | 一文詳解RVB2601套件 助你快速上手赢比賽 新手必看 | RVB2601開發闆快速上手指南 等内容,讓大家全面的了解的RVB2601套件的軟硬體情況,教會大家如何快速上手。今天開始我們将在每期【技術解碼】中為大家介紹RVB2601開發實戰系列例程,今天為大家帶來Helloworld最小系統。

1. 引言

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

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

建議在在看本文之前,先詳細看下

,本例程名為ch2601_helloworld_demo,可以通過CDK直接從OCC拉取。  

2. 最小系統移植适配  

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

2.1 适配YoC 核心

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

RVB2601應用開發實戰系列一: Helloworld最小系統

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

2.1.1 任務切換相關

  • cpu_intrpt_switch

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

1. cpu_intrpt_switch:
2.     li      t0, 0xE080100C
3.     lb      t1, (t0)
4.     li      t2, 0x01
5. or      t1, t1, t2
6.     sb      t1, (t0)
7. 
8.     ret
c      
  • tspend_handler

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

1. tspend_handler:
2.     addi    sp, sp, -124
3. 
4.     sw      x1, 0(sp)
5.     sw      x3, 4(sp)
6.     sw      x4, 8(sp)
7.     sw      x5, 12(sp)
8.     sw      x6, 16(sp)
9.     sw      x7, 20(sp)
10.     sw      x8, 24(sp)
11.     sw      x9, 28(sp)
12.     sw      x10, 32(sp)
13.     sw      x11, 36(sp)
14.     sw      x12, 40(sp)
15.     sw      x13, 44(sp)
16.     sw      x14, 48(sp)
17.     sw      x15, 52(sp)
18.     sw      x16, 56(sp)
19.     sw      x17, 60(sp)
20.     sw      x18, 64(sp)
21.     sw      x19, 68(sp)
22.     sw      x20, 72(sp)
23.     sw      x21, 76(sp)
24.     sw      x22, 80(sp)
25.     sw      x23, 84(sp)
26.     sw      x24, 88(sp)
27.     sw      x25, 92(sp)
28.     sw      x26, 96(sp)
29.     sw      x27, 100(sp)
30.     sw      x28, 104(sp)
31.     sw      x29, 108(sp)
32.     sw      x30, 112(sp)
33.     sw      x31, 116(sp)
34.     csrr    t0, mepc
35.     sw      t0, 120(sp)
36. 
37.     la      a1, g_active_task
38.     lw      a1, (a1)
39.     sw      sp, (a1)
40. 
41.     li      t0, 0xE000E100
42.     lw      t1, (t0)
43.     li      t2, 0xFEFFFFFF
44. and     t1, t1, t2
45.     sw      t1, (t0)
46. 
47. __task_switch_nosave:
48.     la      a0, g_preferred_ready_task
49.     la      a1, g_active_task
50.     lw      a2, (a0)
51.     sw      a2, (a1)
52. 
53.     lw      sp, (a2)
54. 
55. /* Run in machine mode */
56.     li      t0, MSTATUS_PRV1
57.     csrs    mstatus, t0
58. 
59.     lw      t0, 120(sp)
60.     csrw    mepc, t0
61. 
62.     lw      x1, 0(sp)
63.     lw      x3, 4(sp)
64.     lw      x4, 8(sp)
65.     lw      x5, 12(sp)
66.     lw      x6, 16(sp)
67.     lw      x7, 20(sp)
68.     lw      x8, 24(sp)
69.     lw      x9, 28(sp)
70.     lw      x10, 32(sp)
71.     lw      x11, 36(sp)
72.     lw      x12, 40(sp)
73.     lw      x13, 44(sp)
74.     lw      x14, 48(sp)
75.     lw      x15, 52(sp)
76.     lw      x16, 56(sp)
77.     lw      x17, 60(sp)
78.     lw      x18, 64(sp)
79.     lw      x19, 68(sp)
80.     lw      x20, 72(sp)
81.     lw      x21, 76(sp)
82.     lw      x22, 80(sp)
83.     lw      x23, 84(sp)
84.     lw      x24, 88(sp)
85.     lw      x25, 92(sp)
86.     lw      x26, 96(sp)
87.     lw      x27, 100(sp)
88.     lw      x28, 104(sp)
89.     lw      x29, 108(sp)
90.     lw      x30, 112(sp)
91.     lw      x31, 116(sp)
92. 
93.     addi    sp, sp, 124
94.     mret
c      

2.1.2 第一個任務初始化

  • cpu_first_task_start

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

1. cpu_first_task_start:
2.     j       __task_switch_nosave
c      
  • cpu_task_stack_init

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

1. void *cpu_task_stack_init(cpu_stack_t *stack_base, size_t stack_size,
2. void *arg, task_entry_t entry)
3. {
4. cpu_stack_t *stk;
5. register int *gp asm("x3");
6. uint32_t temp = (uint32_t)(stack_base + stack_size);
7. 
8.     temp &= 0xFFFFFFF8UL;
9. 
10.     stk = (cpu_stack_t *)temp;
11. 
12.     *(--stk) = (uint32_t)entry;                   /* PC            */
13.     *(--stk) = (uint32_t)0x31313131L;             /* X31           */
14.     *(--stk) = (uint32_t)0x30303030L;             /* X30           */
15.     *(--stk) = (uint32_t)0x29292929L;             /* X29           */
16.     *(--stk) = (uint32_t)0x28282828L;             /* X28           */
17.     *(--stk) = (uint32_t)0x27272727L;             /* X27           */
18.     *(--stk) = (uint32_t)0x26262626L;             /* X26           */
19.     *(--stk) = (uint32_t)0x25252525L;             /* X25           */
20.     *(--stk) = (uint32_t)0x24242424L;             /* X24           */
21.     *(--stk) = (uint32_t)0x23232323L;             /* X23           */
22.     *(--stk) = (uint32_t)0x22222222L;             /* X22           */
23.     *(--stk) = (uint32_t)0x21212121L;             /* X21           */
24.     *(--stk) = (uint32_t)0x20202020L;             /* X20           */
25.     *(--stk) = (uint32_t)0x19191919L;             /* X19           */
26.     *(--stk) = (uint32_t)0x18181818L;             /* X18           */
27.     *(--stk) = (uint32_t)0x17171717L;             /* X17           */
28.     *(--stk) = (uint32_t)0x16161616L;             /* X16           */
29.     *(--stk) = (uint32_t)0x15151515L;             /* X15           */
30.     *(--stk) = (uint32_t)0x14141414L;             /* X14           */
31.     *(--stk) = (uint32_t)0x13131313L;             /* X13           */
32.     *(--stk) = (uint32_t)0x12121212L;             /* X12           */
33.     *(--stk) = (uint32_t)0x11111111L;             /* X11           */
34.     *(--stk) = (uint32_t)arg;                     /* X10           */
35.     *(--stk) = (uint32_t)0x09090909L;             /* X9            */
36.     *(--stk) = (uint32_t)0x08080808L;             /* X8            */
37.     *(--stk) = (uint32_t)0x07070707L;             /* X7            */
38.     *(--stk) = (uint32_t)0x06060606L;             /* X6            */
39.     *(--stk) = (uint32_t)0x05050505L;             /* X5            */
40.     *(--stk) = (uint32_t)0x04040404L;             /* X4            */
41.     *(--stk) = (uint32_t)gp;                      /* X3            */
42.     *(--stk) = (uint32_t)krhino_task_deathbed;    /* X1            */
43. 
44. return stk;
45. }
c      

2.1.3 核心心跳時鐘初始化

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

  • SystemInit

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

1. void SystemInit(void)
2. {
3.     enable_theadisaee();
4. 
5.     cache_init();
6. 
7.     section_init();
8. 
9.     interrupt_init();
10. 
11.     soc_set_sys_freq(CPU_196_608MHZ);
12. 
13.     csi_etb_init();
14. 
15.     sys_dma_init();
16. 
17.     csi_tick_init();
18. 
19. #ifdef CONFIG_XIP
20.     sys_spiflash_init();
21. #endif
22.     bootrom_uart_uninit();
23. }
c      
  • csi_tick_init

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

1. csi_error_t csi_tick_init(void)
2. {
3. csi_error_t ret;
4. 
5.     csi_tick = 0U;
6.     ret = csi_timer_init(&tick_timer, CONFIG_TICK_TIMER_IDX);
7. 
8. if (ret == CSI_OK) {
9.         ret = csi_timer_attach_callback(&tick_timer, tick_event_cb, NULL);
10. 
11. if (ret == CSI_OK) {
12.             ret = csi_timer_start(&tick_timer, (1000000U / CONFIG_SYSTICK_HZ));
13.         }
14.     }
15. 
16. return ret;
17. }
18. 
19. void csi_tick_increase(void)
20. {
21.     csi_tick++;
22. }
23. 
24. static void tick_event_cb(csi_timer_t *timer_handle, void *arg)
25. {
26.     csi_tick_increase();
27. #if defined(CONFIG_KERNEL_RHINO)
28.     krhino_tick_proc();
29. #elif defined(CONFIG_KERNEL_FREERTOS)
30.     xPortSysTickHandler();
31. #elif defined(CONFIG_KERNEL_UCOS)
32.     OSTimeTick();
33. #endif
34. }
c      

2.1.4 核心初始化

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

  • aos_init

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

1. int pre_main(void)
2. 
3. {
4. /* kernel init */
5.     aos_init();
6. #ifdef CONFIG_OS_TRACE
7.     trace_init_data();
8. #endif
9. 
10. /* init task */
11.     aos_task_new_ext(&app_task_handle, "app_task", application_task_entry,
12. NULL, INIT_TASK_STACK_SIZE, AOS_DEFAULT_APP_PRI);
13. 
14. /* kernel start */
15.     aos_start();
16. 
17. return 0;
18. }
c      
  • aos_start

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

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

2.2 開發helloworld程式

2.2.1 序列槽初始化

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

1. void board_yoc_init()
2. {
3.     board_init();
4. // uart_csky_register(CONSOLE_UART_IDX);
5.     console_init(CONSOLE_UART_IDX, 115200, 128);
6. 
7.     ulog_init();
8.     aos_set_log_level(AOS_LL_DEBUG);
9. 
10.     LOGI(TAG, "Build:%s,%s",__DATE__, __TIME__);
11.     board_cli_init();
12. }
c      
  • console_init

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

  • ulog_init

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

2.2.2 列印Helloworld

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

1. int main(void)
2. {
3.     board_yoc_init();
4.     LOGD(TAG, "%s\n", aos_get_app_version());
5. 
6. while (1) {
7.         LOGD(TAG, "Hello world! YoC");
8.      sample_test();
9.         aos_msleep(1000);
10.     }
11. 
12. return 0;
13. }
c      

2.3. 編譯運作

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

RVB2601應用開發實戰系列一: Helloworld最小系統

3. 總結

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

繼續閱讀