单次执行线程的创建和删除,以及注意事项

    目前工程中需要添加ADC校准代码,在以前的线程中增加的话3个线程中都要分别增加,任务分散在三个线程中,违反了模块化的设计原则,所以最好单独创建一个ADC校准线程。单独创建校准线程实现了模块化,也带了系统的资源消耗,因为必须使用使用信号、事件组等系统资源,对线程进行同步,保证其他线程的作业都执行完了,到达一个同步点,这个同步点是停止自身的作业运行,转而等待校准的完成。

    这个线程只在出厂ADC校准的时候运行,其他时间不会去运行,因此使用动态创建的方法,当上位机下发开始校准指令后,创建校准任务,校准完成后,点击上位机的停止校准按键,此时MCU会将k、b参数保存到EEPROM中,清除事件组的标记,完成后会自动删除此校准进程。

    代码如下,当上位机通过modbus指令下发0x01时,通过rt_thread_create( )创建校准线程,防止上位机按键0x01指令多次触发,定义了一个静态局部变量 static rt_uint8_t adc_cal_task_cnt = 0;保证创建任务只执行一次,否则多次执行的话会导致程序混乱,创建多个相同的线程实例会很快耗尽MCU的资源。

    当上位机发送0x02时表示校准完成,清零adc_cal_task_cnt。

    if( ((rt_uint16_t *)(&armcalpara) + index + i) > &armcalpara.set_cal) // click calibration button  // 校准触发后所有的采样线程都要停止,进行校准, 正在进行的
    {
        switch(armcalpara.set_cal)
        {
            case 0: // NULL, do nothing
                   break;

            case 1: // start calibration
                    // create new task to calibration

                    if(adc_cal_task_cnt < 1)
                    {
                        adc_cal_task_cnt++;

                        adc_cal_tid = rt_thread_create("adc_cal", adc_cal_thread_entry, NULL, ADC_CAL_THREAD_STACK_SIZE, ADC_CAL_THREAD_PRIO, 30);
                        if (adc_cal_tid == NULL)
                        {
                            LOG_E("adc_cal thread create fail!");
                            return -RT_ERROR;
                        }

                        rt_thread_startup(adc_cal_tid);

                        LOG_D("start to calibration");
                    }
                    else
                    {
                        LOG_W("calibration has exist");
                    }

                    break;

            case 2: // finish calibration
                    adc_cal_task_cnt = 0;
                    LOG_D("finish calibration");
                    break;
        }
    }

    return 0;

    在校准线程中,当接收到完成校准指令0x02,先通过rt_thread_self( )获得线程句柄,然后调用系统函数:rt_task_delete(tid) 删除自身线程,此时线程占用的任务栈空间会全部释放,归还给系统RAM,代码如下:

        else
        if(0x02 == armcalpara.set_cal)
        {
            // if calibration has finished, send semphore to all threads to run
            rt_sem_release(adc_cal_ovr_sem);
            rt_sem_release(adc_cal_ovr_sem);
            rt_sem_release(adc_cal_ovr_sem);


            // store k、b value in EEPROM
            // if value modify, run once. else do not restore

            rt_event_recv(adc_cal_event, (EVENT_FLAG0 | EVENT_FLAG1 | EVENT_FLAG2 | EVENT_FLAG3),\
                          RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_NO, NULL);  // clear all event, next time will be used

            rt_thread_t rt_tid = rt_thread_self();
            rt_thread_delete(rt_tid);         // delete self task
        }

    这里可以知道,当在校准线程内部创建的信号量或者事件组时,随着自身线程栈TCB的释放,这些信号量和事件组也会被释放掉,因为是在线程栈内部创建的资源。当其他线程等待校准线程内部创建的信号量进行了阻塞时,随着线程删除会出现等待信号量错误,因此要避免这种情况,在初始化时要先创建,避免在其他线程使用时出现上面说的错误,比如使用了信号量和事件组,可以这样初始化,代码如下:

static int adc_cal_init(void)
{
    adc_cal_event = rt_event_create("cal_event", RT_IPC_FLAG_FIFO);

    adc_cal_ovr_sem = rt_sem_create("adc_cal_ovr_sem", 0, RT_IPC_FLAG_FIFO);

    return 0;
}
INIT_APP_EXPORT(adc_cal_init);

删除线程 (rt_thread_delete)
资源释放:当调用rt_thread_delete函数时,系统会释放与线程相关联的所有资源,包括线程的栈空间和控制块(TCB)。
生命周期:删除线程意味着线程的完全终止。一旦线程被删除,它的控制块将被移除,线程将不复存在。
调用时机:通常在线程不再需要运行,或者线程已经完成了它的任务并且不再需要保留时,调用此函数。
线程状态:如果线程正在运行,调用rt_thread_delete后,线程将在下一个调度点停止执行并被删除。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值