初中生
最后登录1970-1-1
在线时间 小时
注册时间2023-6-30
|
本帖最后由 桃子念peach 于 2023-8-24 11:48 编辑
一、简介
1.1 项目简介
SimpleRTOS(catOS) 是我大学实习期间为了学习 RTOS 编写的一个简单的内核,主要调度方式基于优先级抢占,该项目重构了两次,故内容和功能有所不同,最新版本仅保留了固定优先级调度方式。
本次项目内容为将该内核对野火的瑞萨启明6M5开发板进行适配,并编写简单的demo验证正确性。
第一次发帖,如果有什么错误请大家批评指正。
1.2 开发板简介
官网特性介绍如下
支持 TrustZone® 的 200MHz Arm® Cortex®-M33
安全芯片功能
1MB - 2MB 闪存、448KB 支持奇偶校验的 SRAM 和 64KB ECC SRAM
具有后台运行能力的双区闪存,以及存储块交换功能
8KB 数据闪存,提供与 EEPROM 类似的数据存储功能
100 引脚封装至 176 引脚封装
带有专用 DMA 的以太网控制器
电容触摸按键感应单元
高速和全速 USB 2.0
CAN FD(也支持CAN 2.0B)
QuadSPI 和 OctaSPI
SCI多功能串口(UART、简单 SPI、简单 I2C)
SPI/ I2C 多主接口
SDHI 和 MMC
1.3 内核设计概述
1.3.1 总体架构设计
- 抢占式优先级调度
- 可移植层
- 重写更简易更轻量级的部分库函数
- 除内核外还提供部分组件
- 简易的`shell`程序
- 设备驱动框架
- 内存地址读写功能
1.3.2 优先级调度策略
内核中固定优先级调度使用了位图+链表队列的形式,利用位图寻找最高优先级任务并以有序链表的形式进行调度,并且在信号量等临界资源访问上设计了优先级天花板防止优先级反转
1.3.3 内核实现了一个以组件形式存在的 shell 命令行界面,该 shell 作为一个任务运行在 os 之上
二、适配内核
2.1 可移植接口概览
需要实现的可移植接口包括以下部分- /**
- * @brief 硬件初始化
- */
- void cat_hw_init(void);
- /**
- * @brief 开始调度
- *
- */
- void catos_start_sched(void);
- /**
- * @brief 上下文切换
- *
- */
- //void cat_hw_context_switch(void);
- /**
- * @brief 上下文切换
- *
- * @param from_task_sp_addr 上一个任务tcb中堆栈指针变量的 *地址*
- * @param to_task_sp_addr 下一个任务tcb中堆栈指针变量的 *地址*
- */
- void cat_hw_context_switch(cat_uint32_t from_task_sp_addr, cat_uint32_t to_task_sp_addr);
- /**
- * @brief 切换到第一个任务的上下文
- *
- * @param first_task_sp_addr 要切换的任务tcb中堆栈指针变量的 *地址*
- */
- void cat_hw_context_switch_to_first(cat_uint32_t first_task_sp_addr);
- /**
- * @brief 关中断进临界区
- *
- * @return cat_uint32_t
- */
- cat_uint32_t cat_hw_irq_disable(void);
- /**
- * @brief 开中断出临界区
- *
- * @param status
- */
- void cat_hw_irq_enable(cat_uint32_t status);
- /**
- * @brief 栈初始化
- *
- * @param task_entry 任务入口函数地址
- * @param parameter 参数
- * @param stack_addr 栈起始地址
- * @param exit 任务退出函数地址
- * @return cat_uint8_t* 初始化后的栈顶地址
- */
- cat_uint8_t *cat_hw_stack_init(void *task_entry, void *parameter, cat_uint8_t *stack_addr, void *exit);
复制代码
2.2 硬件初始化
在硬件初始化中主要是设置系统时钟中断频率,初始化时设置时钟中断为关闭状态
- /**
- * @brief 硬件初始化
- */
- void cat_hw_init(void)
- {
- /* 设置系统时钟中断频率为100Hz(每秒100次) */
- cat_set_systick_period(CATOS_SYSTICK_MS);
- }
- /**
- * @brief 初始化时钟中断
- *
- * @param ms 每个tick的时间(ms)
- */
- static void cat_set_systick_period(cat_uint32_t ms)
- {
- cat_uint32_t err = 0;
- cat_uint32_t IT_Period = 0;
- IT_Period = ms * SystemCoreClock / 1000;
- //err = SysTick_Config(IT_Period);
- /* 如果设定的周期太离谱就停在这 */
- if ((IT_Period - 1UL) > SysTick_LOAD_RELOAD_Msk)
- {
- err = 1;
- }
- assert(0 == err);
- SysTick->LOAD = (uint32_t)(IT_Period - 1UL); /* 设置重装载寄存器 */
- NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* 设置时钟中断优先级 */
- SysTick->VAL = 0UL; /* 设置计数器装载值 */
- SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | /* 设定为内核时钟FCLK */
- SysTick_CTRL_TICKINT_Msk | /* 设定为systick计数器倒数到0时触发中断 */
- ~SysTick_CTRL_ENABLE_Msk; /* 关闭定时器中断,若创建任务则在catos_start_sched()中开启该中断 */
- }
复制代码
2.3 开始调度与切换到第一个任务的上下文
开始调度需要从就绪表中获取最高优先级任务并设置为当前任务,并且在恢复第一个任务上下文之前需要打开时钟中断并初始化 pendsv 中断以保证调度的正常工作。
注:这里暂时没有对不使用 fpu 的情况适配,参考 freertos 的可移植接口实现。
- /**
- * @brief 开始调度
- *
- */
- void catos_start_sched(void)
- {
- cat_uint32_t tmp_reg = 0;
- struct _cat_task_t *first_task = NULL;
- /* 获取最高优先级任务 */
- first_task = cat_sp_task_highest_ready();
- /* 因为是第一个任务,不用像调度时判断是否和上一个任务一样,直接赋值给当前任务就行 */
- cat_sp_cur_task = first_task;
- /* 允许调度(打开调度锁,并且不在该处进行调度) */
- cat_sp_task_sched_enable_without_sched();
- /* 开启时钟中断 */
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
- /* 设置pendsv中断优先级 */
- tmp_reg = MEM32(NVIC_SHPR3);
- tmp_reg |= NVIC_PENDSV_PRI;
- MEM32(NVIC_SHPR3) = tmp_reg;
- /* 切换到第一个任务 */
- cat_hw_context_switch_to_first((cat_uint32_t)&(first_task->sp));
- }
- /**
- * @brief 切换到第一个任务的上下文
- *
- */
- void cat_hw_context_switch_to_first(void)
- {
- __enable_irq();
- __ISB();
- /* 各个寄存器地址 */
- __asm volatile (
- ".equ SCB_ICSR, 0xE000ED04 \n" // 中断控制寄存器
- ".equ SCB_VTOR, 0xE000ED08 \n" // 中断向量表偏移寄存器
- ".equ ICSR_PENDSVSET, 0x10000000 \n" // pendsv触发值
- ".equ SHPR3_PRI_14, 0xE000ED22 \n" // 系统异常handler优先级寄存器 3 (PendSV).
- ".equ PRI_LVL_PENDSV, 0xFF \n"// pendsv优先级 (最低).
- ".equ SHPR3_PRI_15, 0xE000ED23 \n"// 系统异常handler优先级寄存器 3 (Systick).
- "ldr r1, =cat_context_to_task_sp_ptr \n"
- "str r0, [r1] \n"
- #if __FPU_USED
- //#error "__FPU_USED"
- /* 清除control寄存器的FPCA */
- "mrs r2, control \n" /* read */
- "bic r2, r2, #0x04 \n" /* modify */
- "msr control, r2 \n" /* write-back */
- #else
- #error "must use fpu"
- #endif
- /* 将变量 cat_context_from_task_sp_ptr 设置为0*/
- "ldr r1, =cat_context_from_task_sp_ptr \n"
- "mov r0, #0 \n"
- "str r0, [r1] \n"
- "mov r4, #0x1234 \n"
- /* 触发pendsv中断,允许中断后会立即进入pendsv切换 */
- "ldr r0, =SCB_ICSR \n"
- "ldr r1, =ICSR_PENDSVSET \n"
- "str r1, [r0] \n" /* *(SCB_ICSR) = "ICSR_PENDSVSET */
- /* 不会到达这里 */
- "dsb \n"
- "isb \n"
- "svc 0 \n"
- );
- }
复制代码
2.4 上下文切换
上下文切换使用 pendsv 中断进行,主要工作为保存当前任务堆栈和寄存器以及恢复下一个任务的堆栈和寄存器。
- /**
- * void cat_hw_context_switch(void)
- * 触发pendsv中断进行任务切换(pendsv的优先级在开始第一个任务时已经设置)
- */
- .global cat_hw_context_switch
- .type cat_hw_context_switch, %function
- cat_hw_context_switch:
- /* 将两个任务的堆栈指针变量的 *地址* 加载到临时变量中 */
- /* cat_context_from_task_sp_ptr = &(cat_sp_cur_task->sp) */
- ldr r2, =cat_context_from_task_sp_ptr
- str r0, [r2]
- /* cat_context_to_task_sp_ptr = &(cat_sp_next_task->sp) */
- ldr r2, =cat_context_to_task_sp_ptr
- str r1, [r2]
- /* 触发pendsv中断进行切换 */
- ldr r0, =SCB_ICSR
- ldr r1, =ICSR_PENDSVSET
- str r1, [r0] /* *(SCB_ICSR) = ICSR_PENDSVSET */
- bx lr
- void PendSV_Handler(void)
- {
- __asm volatile (
- /* 关闭全局中断并保存当前中断屏蔽寄存器中的值方便恢复 */
- "mrs r2, primask \n"
- "cpsid i \n"
- /* 保存中断屏蔽寄存器状态 */
- "push {r2} \n"
- /* 获取from任务的堆栈指针变量中的值 */
- /* 在进入pendsv之前 cat_context_from_task_sp_ptr = &(from_task->sp) */
- /** 故有:
- * r0 = &(from_task->sp)
- * r1 = *(&(from_task->sp)) 等价于 r1 = from_task->sp
- */
- "ldr r0, =cat_context_from_task_sp_ptr \n"
- "ldr r1, [r0] \n"
- /* 如果为零则说明是第一个任务 */
- "cbz r1, switch_to_thread \n"
- /* 暂时可能用不到trustzone, 因此直接跳转 */
- "b contex_ns_store \n"
- /* 暂时可能用不到 END */
- "contex_ns_store: \n"
- /* 用户级堆栈是psp,特权级堆栈是msp */
- /* 任务用的是psp,将当前寄存器保存到堆栈中 */
- "mrs r1, psp \n"
- #if __FPU_USED
- "tst lr, #0x10 \n"
- "it eq \n"
- "vstmdbeq r1!, {s16-s31} \n"
- #if BSP_TZ_NONSECURE_BUILD
- #else
- /* Stack R4-R11 on the process stack. Also stack LR since the FPU is supported. */
- "STMDB R1!, {R4-R11, LR} \n"
- #endif
- #else
- #error "must use fpu"
- #endif
- #if BSP_TZ_NONSECURE_BUILD
- #error "should not use BSP_TZ_NONSECURE_BUILD"
- #elif RM_CATOS_PORT_PSPLIM_PRESENT
- "mrs r3, psplim \n" /* R3 = PSPLIM. */
- "stmdb r1!, {r3} \n"
- #endif
- /* 记录最后的指针到任务栈curstk->stack */
- /* 更新tcb的堆栈指针变量值 */
- /**
- from_task->sp = r1
- */
- "ldr r0, [r0] \n"
- "str r1, [r0] \n"
- #if !__FPU_USED
- #error "must use fpu"
- #endif
- /* 上下文保存结束 */
-
- "switch_to_thread: \n"
- "ldr r1, =cat_context_to_task_sp_ptr \n"
- "ldr r1, [r1] \n"
- "ldr r1, [r1] \n"
- #if BSP_TZ_NONSECURE_BUILD
- #error "not support BSP_TZ_NONSECURE_BUILD"
- #elif RM_CATOS_PORT_PSPLIM_PRESENT
- "LDMIA R1!, {R2} \n" /* R1 = PSPLIM */
- "MSR PSPLIM, R2 \n" /* Restore the PSPLIM register value for the task. */
- #endif
- #if BSP_TZ_NONSECURE_BUILD
- #error "not support BSP_TZ_NONSECURE_BUILD"
- #endif
- "b contex_ns_load \n"
- "contex_ns_load: \n"
- #if __FPU_USED
- #if BSP_TZ_NONSECURE_BUILD
- #error "not support BSP_TZ_NONSECURE_BUILD"
- #else
- /* Restore R4-R11 and LR from the process stack. */
- "LDMIA R1!, {R4-R11, LR} \n"
- #endif
- /* Check to see if the thread being restored is using the FPU. If so, restore S16-S31. */
- "TST LR, #0x10 \n"
- "IT EQ \n"
- "VLDMIAEQ R1!, {S16-S31} \n"
- #else
- #error "must use fpu"
- #endif
- "pendsv_exit: \n"
- "msr psp, r1 \n"
- #if !__FPU_USED
- #error "must use fpu"
- #endif
- "pop {r2} \n"
- /* 恢复屏蔽寄存器值 */
- "msr primask, r2 \n"
- #if __FPU_USED
- "bx lr \n"
- #else
- #error "must use fpu"
- #endif
- );
- }
复制代码
2.5 临界区的开关中断
开关中断主要是对primask的操作,和 cortex-m3 差不多,比较简单。
- /**
- * cat_uint32_t cat_hw_irq_disable(void)
- * 关中断方式进入临界区
- * primask-->r0
- */
- .global cat_hw_irq_disable
- .type cat_hw_irq_disable, %function
- cat_hw_irq_disable:
- mrs r0, primask /* ret = primask */
- cpsid I /* disable irq */
- bx lr /* return ret */
- /**
- * void cat_hw_irq_enable(cat_uint32_t status)
- * 开中断方式出临界区
- * r0-->status
- */
- .global cat_hw_irq_enable
- .type cat_hw_irq_enable, %function
- cat_hw_irq_enable:
- msr primask, r0 /* primask = status */
- bx lr
复制代码
2.6 任务栈初始化
在任务创建时需要根据任务的相关信息对任务栈帧中各项进行初始化,包括设置 psr 寄存器、任务入口地址、退出函数地址和任务参数。
需要特别注意的是 cortex-m33 还需要正确设置 psplim 等寄存器。
- /**
- * @brief 栈初始化
- *
- * @param task_entry 任务入口函数地址
- * @param parameter 参数
- * @param stack_addr 栈起始地址
- * @param exit 任务退出函数地址
- * @return cat_uint8_t* 初始化后的栈顶地址
- */
- cat_uint8_t *cat_hw_stack_init(void *task_entry, void *arg, cat_uint8_t *stack_addr, void *exit_func)
- {
- struct _stack_frame *stack_frame;
- cat_uint32_t *stack;
- cat_uint32_t i;
- /* 先加上4字节再8字节向下取整对齐(相当于四舍五入) */
- stack = stack_addr + sizeof(cat_uint32_t);
- stack = (cat_uint8_t *)CAT_ALIGN_DOWN((cat_uint32_t)stack, 8);
- /* task context saved & restore by hardware: */
- *(--stack) = (cat_uint32_t)0x01000000L; /* xPSR: EPSR.T = 1, thumb mode */
- *(--stack) = (cat_uint32_t)task_entry; /* Entry Point */
- *(--stack) = (cat_uint32_t)exit_func; /* R14 (LR) */
- *(--stack) = (cat_uint32_t)0x12121212L; /* R12 */
- *(--stack) = (cat_uint32_t)0x03030303L; /* R3 */
- *(--stack) = (cat_uint32_t)0x02020202L; /* R2 */
- *(--stack) = (cat_uint32_t)0x01010101L; /* R1 */
- *(--stack) = (cat_uint32_t)arg; /* R0 : argument */
- #if __FPU_USED && !BSP_TZ_NONSECURE_BUILD
- *(--stack) = (cat_uint32_t)portINITIAL_EXC_RETURN; /* exe_return值 */
- #endif
- *(--stack) = (cat_uint32_t)0x11111111L; /* R11 */
- *(--stack) = (cat_uint32_t)0x10101010L; /* R10 */
- *(--stack) = (cat_uint32_t)0x09090909L; /* R9 */
- *(--stack) = (cat_uint32_t)0x08080808L; /* R8 */
- *(--stack) = (cat_uint32_t)0x07070707L; /* R7 */
- *(--stack) = (cat_uint32_t)0x06060606L; /* R6 */
- *(--stack) = (cat_uint32_t)0x05050505L; /* R5 */
- *(--stack) = (cat_uint32_t)0x04040404L; /* R4 */
- #if RM_CATOS_PORT_PSPLIM_PRESENT
- *(--stack) = (cat_uint32_t)0x00; /* psplim */
- #endif
- stack_frame = (struct _stack_frame *)stack;
- #endif /* #if 0 */
- /* 返回当前栈指针 */
- return stack;
- }
复制代码
三、板载led和串口的驱动
3.1 板载led(GPIO)
ra6m5 的引脚结构体定义
- typedef struct _ra6m5_fire_pin_t
- {
- cat_uint32_t pin_num;
- ioport_instance_ctrl_t *p_ctrl_ptr;
- const bsp_io_port_pin_t gpio_pin;
- }ra6m5_fire_pin_t;
复制代码 暂时只用得上输出引脚,实现 pin_write 接口,利用 fsp 库实现引脚输出
- cat_uint8_t cat_pin_write(cat_uint32_t pin_num, cat_uint8_t val)
- {
- cat_uint8_t ret = CAT_ERROR;
- ra6m5_fire_pin_t *p = &(pin_map[0]);
-
- /* 遍历pin_map数组 */
- for(; p->pin_num!=0xffff; p++)
- {
- if(p->pin_num == pin_num)
- {
- if(0 == val)
- {
- R_IOPORT_PinWrite(p->p_ctrl_ptr, p->gpio_pin, BSP_IO_LEVEL_LOW);
- ret = CAT_EOK;
- }
- else if(1 == val)
- {
- R_IOPORT_PinWrite(p->p_ctrl_ptr, p->gpio_pin, BSP_IO_LEVEL_HIGH);
- ret = CAT_EOK;
- }
- else
- {
- /* 非法值,之后打印错误信息 */
- ret = CAT_ERROR;
- while(1);
- }
-
- /* 写入结束结束循环 */
- break;
- } /* if */
- } /* for */
- return ret;
- }
复制代码
3.2 串口
野火教程以及我找到的官方例程中 ra6m5 的串口收发均使用中断实现,但在本内核中的标准输入输出函数大部分是以字符为单位,故发送接口用寄存器方式实现,串口接收仍然使用 fsp 库 + 中断实现
3.2.1 串口发送
- static cat_int8_t ra6m5_uart_send_char(cat_device_t*dev, cat_uint32_t timeout, cat_uint8_t data)
- {
- cat_int8_t ret = CAT_ERROR;
- struct _cat_ra6m5_fire_uart_private_data_t *private_data = NULL;
- /* 获取设备实例数据 */
- private_data = (struct _cat_ra6m5_fire_uart_private_data_t *)(dev->pri_data);
- sci_uart_instance_ctrl_t *p_ctrl = private_data->inst_ctrl_ptr;
- /* 将要发送的数据放进数据寄存器 */
- p_ctrl->p_reg->TDR = data;
- /* 等待发送完成或超时 */
- while(
- ((p_ctrl->p_reg->SSR_b.TEND) == 0) &&
- (0 != timeout)
- )
- {
- timeout--;
- }
- /* 未超时才成功 */
- if(0 != timeout)
- {
- ret = CAT_EOK;
- }
- return ret;
- }
- static cat_uint32_t ra6m5_uart_send(cat_device_t*dev, uint32_t timeout, uint8_t const * const buffer, uint32_t const size)
- {
- (void)timeout;
- cat_uint32_t cnt = 0;
- cat_int8_t err = CAT_EOK;
-
- while(
- (CAT_EOK == err) &&
- (cnt < size)
- )
- {
- err = ra6m5_uart_send_char(dev, 0xffff, buffer[cnt]);
- cnt++;
- }
- /* 因为前面在一次 while 循环中无论发送是否成功 cnt 都会无条件加一,所以如果失败了就有一个多加上的计数 */
- if(CAT_ERROR == err)
- {
- cnt--;
- }
- return cnt;
- }
复制代码 3.2.2 串口接收
- static cat_uint32_t ra6m5_uart_recv(cat_device_t*dev, uint32_t timeout, uint8_t *buffer, uint32_t const size)
- {
- cat_uint32_t recv_buffer_idx = 0; /**< 串口接收缓冲区访问索引 */
- cat_uint32_t err = CAT_ERROR;
- struct _cat_ra6m5_fire_uart_private_data_t *private_data = NULL;
- /* 获取设备实例数据 */
- private_data = (struct _cat_ra6m5_fire_uart_private_data_t *)(dev->pri_data);
- assert(NULL != private_data);
- /* 读取 */
- while(recv_buffer_idx != size)
- {
- while(
- (false == uart4_receive_char) &&
- (0 != timeout)
- )
- {
- timeout--;
- };
- if(0 == timeout)
- {
- break;
- }
- while(
- (cat_ringbuffer_is_empty(private_data->p_ringbuffer) == 0)
- )
- {
- /* 获取接收到的字符 */
- err = cat_ringbuffer_get(private_data->p_ringbuffer, &(buffer[recv_buffer_idx++]));
- if(CAT_ERROR == err)
- {
- /* 获取失败,因为while条件判断过非空,所以出大问题 */
- while(1);
- }
- if(recv_buffer_idx == size)
- {
- break;
- }
- }
- if(cat_ringbuffer_is_empty(private_data->p_ringbuffer))
- {
- /* 取完才改遍flag */
- uart4_receive_char = false;
- }
- }
- return recv_buffer_idx;
- }
复制代码
3.2.3 串口中断服务函数
- /* uart4中断回调函数 */
- void debug_uart4_callback(uart_callback_args_t *pargs)
- {
- switch(pargs->event)
- {
- case UART_EVENT_RX_CHAR:
- {
- cat_ringbuffer_put(&uart4_rb, pargs->data);
- uart4_receive_char = true;
- }
- default:
- {
- break;
- }
- }
- }
复制代码
3.3 基于 GPIO 引脚驱动实现 iic 总线驱动
利用 gpio 的基本输入输出实现软件 iic 功能
3.3.1 设计 iic 数据结构
因为是软件 iic,所以每个总线需要设置 scl 和 sda 引脚,并且总线时序中可能需要不同的 delay 操作。此外,is_init 用来防止重复初始化同一个总线。
- typedef struct _cat_iic_bus_t
- {
- cat_uint32_t scl;
- cat_uint32_t sda;
- cat_bool_t is_init;
- cat_uint32_t delay_us;
- cat_uint32_t timeout_us;
- const cat_uint8_t *name;
- }cat_iic_bus_t;
复制代码 3.3.2 设计引脚电平操作
因为有 GPIO 驱动作为基础,设置引脚输入输出和高低电平直接调用 pin 驱动即可
- #define SET_SCL(bus, val) cat_pin_write((bus)->scl, val)
- #define SET_SDA(bus, val) cat_pin_write((bus)->sda, val)
- #define GET_SCL(bus) cat_pin_read((bus)->scl)
- #define GET_SDA(bus) cat_pin_read((bus)->sda)
- #define SDA_OUT(bus) cat_pin_set_mode((bus)->sda, CAT_PIN_MODE_OUTPUT);
- #define SDA_IN(bus) cat_pin_set_mode((bus)->sda, CAT_PIN_MODE_INPUT);
- #define SCL_L(bus) SET_SCL((bus), CAT_PIN_LOW)
- #define SDA_L(bus) SET_SDA((bus), CAT_PIN_LOW)
- #define SDA_H(bus) SET_SDA((bus), CAT_PIN_HIGH)
复制代码
四、调试验证
4.1 创建 demo 项目
在 projects 目录利用 rasc 工具生成 keil 裸机工程,只需配置 led 引脚以及 uart 串口
4.2 编写程序创建任务
在 hal_entry 中创建用户任务
- #include "hal_data.h"
- #include "catos.h"
- FSP_CPP_HEADER
- void R_BSP_WarmStart(bsp_warm_start_event_t event);
- FSP_CPP_FOOTER
- /*******************************************************************************************************************//**
- * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used. This function
- * is called by main() when no RTOS is used.
- **********************************************************************************************************************/
- #define TASK1_STACK_SIZE (1024)
- #define TASK2_STACK_SIZE (1024)
- struct _cat_task_t task1;
- struct _cat_task_t task2;
- cat_stack_type_t task1_env[TASK1_STACK_SIZE];
- cat_stack_type_t task2_env[TASK2_STACK_SIZE];
- uint32_t sched_task1_times = 0;
- uint32_t sched_task2_times = 0;
- #define BOARD_LED_PIN 0
- void board_led_init(void)
- {
- cat_pin_init(BOARD_LED_PIN, CAT_PIN_MODE_OUTPUT);
- }
- void board_led_on(void)
- {
- cat_pin_write(BOARD_LED_PIN, CAT_PIN_LOW);
- }
- void board_led_off(void)
- {
- cat_pin_write(BOARD_LED_PIN, CAT_PIN_HIGH);
- }
- void task1_entry(void *arg)
- {
- for(;;)
- {
- sched_task1_times++;
- board_led_on();
- cat_sp_task_delay(100);
- board_led_off();
- cat_sp_task_delay(100);
- }
- }
- void task2_entry(void *arg)
- {
- for(;;)
- {
- cat_sp_task_delay(100);
- //CAT_DEBUG_PRINTF("[task2] %d\r\n", catos_systicks);
- }
- }
- void hal_entry(void)
- {
- /* TODO: add your own code here */
- /* 初始化os */
- catos_init();
- /* 利用pin驱动初始化板载led */
- board_led_init();
- /* 测试创建任务运行 */
- cat_sp_task_create(
- (const uint8_t *)"task1_task",
- &task1,
- task1_entry,
- NULL,
- 0,
- task1_env,
- TASK1_STACK_SIZE
- );
- cat_sp_task_create(
- (const uint8_t *)"task2_task",
- &task2,
- task2_entry,
- NULL,
- 0,
- task2_env,
- sizeof(task2_env)
- );
- /* 开始调度 */
- catos_start_sched();
- /* 不会到达这里 */
- while(1);
- #if BSP_TZ_SECURE_BUILD
- /* Enter non-secure code */
- R_BSP_NonSecureEnter();
- #endif
- }
复制代码 4.3 烧写与验证
使用 xshell 连接串口并烧录程序,可以观察到l led 闪烁,并且串口有 shell 的信息,命令可以正常使用。
五、总结
在整个项目过程中,因为涉及底层操作,并且教程还比较少,走了不少弯路,但总体来说还是很不错的,特别是 fsp 配置方面。
建议瑞萨可以在 eestudio 适配更多调试器方便开发者使用;并且在使用野火 dap 时发现其在 ubuntu 下要被识别比较麻烦,如果能改进,使用场景会更多。
六、附件与项目地址
B站视频介绍:【野火】瑞萨RA MCU创意氛围赛 基于优先级的实时RTOS内核_哔哩哔哩_bilibili
Github链接 :Simple-XX/SimpleRTOS at ver2 (github.com)
|
|