RT-Thread 能力认证习题总结 ---- 第一周

RT-Thread 能力认证习题总结 第一周

1、基于自己的开发板,做一个能在自己开发板上跑起来的BSP

参考教程:STM32系列BSP官方制作教程

制作好的工程如下:
RT-Thread 能力认证习题总结 ---- 第一周_第1张图片

2、作业1:根据已经做好的BSP,添加一个PWM外设,参考STM32 系列外设驱动添加指南,并可以使用该PWM外设驱动LED,产生亮灭渐变的效果。

参考教程:个人博客教程

具体驱动LED代码:

#define LED_PIN_NUM         23      /* LED PIN脚编号,查看驱动文件drv_gpio.c确定 */
#define PWM_DEV_NAME        "pwm4"  /* PWM设备名称 */
#define PWM_DEV_CHANNEL     3       /* PWM通道 */

struct rt_device_pwm  *pwm_dev;      /* PWM设备句柄 */

/* 
说明:led闪烁线程的入口函数
功能:实现红色LED的亮度渐变
*/
static void tid_ledflash_entry(void *parameter)
{
    rt_uint32_t period, pulse;
    period = 50000;    /* 周期为0.5ms,单位为纳秒ns */
    pulse = 0;          /* PWM脉冲宽度值,单位为纳秒ns */
    /* 查找设备 */
    pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME);
    if (pwm_dev == RT_NULL)
    {
//        return RT_ERROR;
    }
    /* 设置PWM周期和脉冲宽度默认值 */
    rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
    /* 使能设备 */
    rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
    while (1)
    {
        rt_thread_mdelay(10);
        pulse += 100;      /* 从0值开始每次增加5000ns */
        if (pulse >= period)
        {
            pulse = 0;
        }
        /* 设置PWM周期和脉冲宽度 */
        rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse);
    }
}

3、作业2:使用信号量的方式,同步LED的亮灭,500ms亮 500ms灭

实现过程:建立两个线程,一个每隔500ms释放一个信号量,另一个线程以永久等待的方式获取信号量,一旦获取成功,翻转LED灯的亮灭状态。

代码如下

#define LED_PIN_NUM             24
#define THREAD_PRIORITY         6
#define THREAD_TIMESLICE        10

/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{
   
   while(1)
   {
         rt_sem_release(dynamic_sem);
         rt_thread_mdelay(500);
   }
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread2_entry(void *parameter)
{
    static rt_err_t result;
    static rt_uint8_t reversal_flag = 0;
    
    rt_pin_mode(LED_PIN_NUM, PIN_MODE_OUTPUT);
    while(1)
    {
        /* 永久方式等待信号量,获取到信号量,则执行number自加的操作 */
        result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
        if (result != RT_EOK)
        {        
            rt_kprintf("t2 take a dynamic semaphore, failed.\n");
            rt_sem_delete(dynamic_sem);
            return;
        }
        
        if(reversal_flag)
        {
            rt_pin_write(LED_PIN_NUM, PIN_LOW);
            reversal_flag = 0;
        }
        else
        {
            rt_pin_write(LED_PIN_NUM, PIN_HIGH);
            reversal_flag = 1;
        }
        
    }   
}

/* 信号量示例的初始化 */
void ledthread_init(void)
{
    /* 创建一个动态信号量,初始值是0 */
    dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
    if (dynamic_sem == RT_NULL)
    {
        rt_kprintf("create dynamic semaphore failed.\n");
    }
    rt_thread_init(&thread1,
                   "thread1",
                   rt_thread1_entry,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack), 
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);
               
    rt_thread_init(&thread2,
                   "thread2",
                   rt_thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack), 
                   THREAD_PRIORITY-1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);
}

4、作业3:问答题

4.1 总结RT-Thread的启动流程。

int rtthread_startup(void)
{
	rt_hw_interrupt_disable();
	/* 板 级 初 始 化: 需 在 该 函 数 内 部 进 行 系 统 堆 的 初 始 化 */
	rt_hw_board_init();
	/* 打 印 RT-Thread 版 本 信 息 */
	rt_show_version();
	/* 定 时 器 初 始 化 */
	rt_system_timer_init();
	/* 调 度 器 初 始 化 */
	rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
	/* 信 号 初 始 化 */
	rt_system_signal_init();
#endif
	/* 由 此 创 建 一 个 用 户 main() 线 程 */
	rt_application_init();
	/* 定 时 器 线 程 初 始 化 */
	rt_system_timer_thread_init();
	/* 空 闲 线 程 初 始 化 */
	rt_thread_idle_init();
	/* 启 动 调 度 器 */
	rt_system_scheduler_start();
	/* 不 会 执 行 至 此 */
	return 0;
}

启动流程示意图如下:
RT-Thread 能力认证习题总结 ---- 第一周_第2张图片

4.2 非运行时与运行时的image文件分别是什么样的,请画下来。是谁将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中?

系统中包含main()时,__main 函数调用相应的C运行时库的功能实现需要的数据搬运和初始化。将RW数据从ROM中复制到RAM中,并在RAM中建立ZI数据段。

image文件如下图所示:
RT-Thread 能力认证习题总结 ---- 第一周_第3张图片

4.3 MDK环境下各种数据段存储的什么数据?

  1. Code:代码段,存放程序的代码部分;
  2. RO-data:只读数据段,存放程序中定义的常量;
  3. RW-data:读写数据段,存放初始化为非 0 值的全局变量;
  4. ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
    其中,
    RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
    ROM Size 包含了 Code、RO Data 以及 RW Data,表示烧写程序所占用的 Flash 空间的大小;

4.4 在RT-Thread启动时,关了中断,那么在什么时候开启的中断?

开启的位置是在:
->int rtthread_startup(void)
-> rt_system_scheduler_start();
  ->rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
在rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp)函数中的如下位置:
RT-Thread 能力认证习题总结 ---- 第一周_第4张图片

4.5 总结自动初始化原理。

自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中的rt_components_board_init() 与 rt_components_init()被执行。
  RT-Thread 的自动初始化机制使用了自定义 RTI 符号段,将需要在启动时进行初始化的函数指针放到了该段中,形成一张初始化函数表,在系统启动过程中会遍历该表,并调用表中的函数,达到自动初始化的目的。
  RT-Thread 能力认证习题总结 ---- 第一周_第5张图片
  初始化函数主动通过这些宏接口进行申明,如 INIT_BOARD_EXPORT(rt_hw_usart_init),链接器会自动收集所有被申明的初始化函数,放到 RTI 符号段中,该符号段位于内存分布的 RO 段中,该 RTI符号段中的所有函数在系统初始化时会被自动调用。

4.6 总结BSP制作过程。

1. 复制对应系列 BSP 模板文件夹。
2. 使用 CubeMX 配置工程,配置系统时钟和需要的外设引脚功能。拷贝初始化函数到相关位置。
3. 修改board.h 文件,配置 FLASH 和 RAM 的相关参数。
4. 修改 Kconfig 选项,该选项是为了裁剪rt-thread。
5. 修改工程构建的脚本文件。
6. 修改工程模板,该工程模板对应不同的软件。
7. env 界面输入命令 menuconfig 对工程进行配置,并生成新的 rtconfig.h 文件。
8. 使用ENV工具重新生成工程文件。
9. BSP制作完成。

你可能感兴趣的:(RT-Thread 能力认证习题总结 ---- 第一周)