RVB2601应用开发实战系列一: Helloworld最小系统

简介: 技术解码栏目:是面向开发者详细解读芯片开放社区(OCC)上关于处理器、芯片、基础软件平台、集成开发环境及应用开发平台的相关技术,方便开发者学习及快速上手,提升开发效率。

关于RVB2601,我们已经为大家推送了RISC-V应用创新大赛 | 一文详解RVB2601套件 助你快速上手赢比赛新手必看 | RVB2601开发板快速上手指南等内容,让大家全面的了解的RVB2601套件的软硬件情况,教会大家如何快速上手。今天开始我们将在每期【技术解码】中为大家介绍RVB2601开发实战系列例程,今天为大家带来Helloworld最小系统。

1. 引言

RVB2601开发板是基于CH2601芯片设计的生态开发板,其具有丰富的外设功能和联网功能,可以开发设计出很多有趣的应用。为了开发者更好的了解如何在CH2601上开发应用,本文介绍了如何移植对接CH2601芯片到YoC最小系统,开发第一个我的helloworld程序。

整个开发移植工作,我们都全部基于剑池CDK集成开发环境进行开发。剑池CDK以极简开发为理念,是专业为IoT应用开发打造的集成开发环境。它在不改变用户开发习惯的基础上,全面接入云端开发资源,结合图形化的OSTracer、Profiling等调试分析工具,加速用户产品开发。想要了解更多剑池CDK开发信息,请前往平头哥芯片开发社区里集成开发环境获取更多。可登录OCC“开发中心”--资源下载中进行下载。

建议在在看本文之前,先详细看下新手必看 | RVB2601开发板快速上手指南,本例程名为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等代码实现。具体目录结构如下图:


image.png


由于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开发板快速上手教程),看到串口窗口出现一下打印,说明移植成功。

image.png



3. 总结


RVB2601最小系统hellworld主要实现对YoC系统的内核适配,具备RTOS的基本能力,实现简单的串口打印。后续还有更多精彩的实战案例,敬请期待。

相关文章
|
17天前
|
开发者 C# UED
如何轻松将WinUI控件引入Web应用?Uno Platform实战攻略——从环境搭建到性能优化,一探究竟!
【8月更文挑战第31天】Uno Platform 通过支持 WebAssembly,将 WinUI 控件无缝带入 Web,为多平台开发提供了新途径。本文介绍如何在 Web 中使用 WinUI 控件,包括环境搭建、控件使用、性能优化、样式调整及测试调优,助力开发者打造高质量跨平台应用。
36 0
|
4月前
|
编解码 Java API
Jmeter--控制器--详解,2024年最新系统学Python从零开始
Jmeter--控制器--详解,2024年最新系统学Python从零开始
|
算法 数据安全/隐私保护 芯片
快速入门数字芯片设计,UCSD ECE111(十一)Project的一些注意事项
快速入门数字芯片设计,UCSD ECE111(十一)Project的一些注意事项
122 0
|
C语言 智能硬件
STM32cubeMX详细教学及多个小项目合集(包含RTOS)
STM32cubeMX详细教学及多个小项目合集(包含RTOS)
322 0
|
异构计算
xilinx小实验——vivado纯逻辑编程第一个demo
xilinx小实验——vivado纯逻辑编程第一个demo
358 0
xilinx小实验——vivado纯逻辑编程第一个demo
|
传感器 网络协议 物联网
应用实战精解系列(二十):RVB2601之YoC系统
应用实战精解系列(二十):RVB2601之YoC系统
487 0
|
JSON JavaScript 小程序
HaaS UI小程序解决方案基础教学之六: 第一个自定义JSAPI
HaaS UI内置的JSAPI为虽然JS提供了调用底层系统能力的接口,例如数据存储、网络管理、文件操作等,但是这些内置的JSAPI不能满足用户所有的开发需求。HaaS UI提供了扩展JSAPI的方法,用户可以根据项目需求和平台能力实现不同的扩展,以满足不同的功能需要。用户如果想要自定义一些JSAPI,可以按照本教程进行扩展。
HaaS UI小程序解决方案基础教学之六: 第一个自定义JSAPI
|
JavaScript 前端开发 小程序
HaaS UI小程序解决方案基础教学之二: 搭建第一个UI页面
AliOS Things: 阿里云智能IoT团队自研的物联网操作系统,目前已获得国家 HaaS:全称是Hardware as a Service,阿里云智能IoT团队基于AliOS Things系统推出的硬件即服务 HaaS UI:全称是Hardware as a Service User Interface,是源自AliOS Things操作系统上的一套应用&图形解决方案,支持C/C++和 JS两种开发语言
HaaS UI小程序解决方案基础教学之二: 搭建第一个UI页面
|
芯片 内存技术
应用实战精解系列(五):基于RVB2601的Web播放器
应用实战精解系列(五):基于RVB2601的Web播放器
309 0
应用实战精解系列(五):基于RVB2601的Web播放器