/* @NOT_ACT_USED */ /******************************************************************* RTOS操作系统接口函数,用于对系统底层的部分函数进行重新封装,以方便使用 **********************************************************************/ //#include "stm32f4x7_eth.h" //#include "stm32f4xx_wwdg.h" #include "stm32f4xx_hal.h" #include #include "type.h" #include "os_task.h" #include "task.h" #include "FreeRTOS.h" #include "semphr.h" #include "stm32f407xx.h" #include "core_cm4.h" #include "gps.h" #include "rtc.h" SYS_TASK sys_task = {0,}; S_RUN_TIME run_time = {0xff,0xff,0xff}; //记录系统运行时间 时分秒 最大单位,小时 static TIME_MSG_Q time_msg_queue; const static MSG sec_msg = {MSG_SEC,MSG_NO_NEED_FREE}; const static MSG min_msg = {MSG_MINUTE,MSG_NO_NEED_FREE}; static S_SYS_TIME date_time; static S_MSG_STATE msg_state; extern __IO uint32 sys_ready; static uint32 os_int_nesting = 0; //中断嵌套层级 static TIMER tick_timer[MAX_TIMER_NUM]; #if USE_MEM_DEBUG static S_MEM_STATE mem_state; #endif void sys_para_init(void) { #if USE_MEM_DEBUG //内存统计必须打开开关才有效 extern uint32 __heap_base; //堆首地址 extern uint32 __heap_limit; //堆地址 也是中断栈的首地址 memset((void*)&mem_state,0,sizeof(mem_state)); mem_state.total =(uint32)&__heap_limit- (uint32)&__heap_base; mem_state.used = 0; #endif memset((void*)&msg_state,0,sizeof(msg_state)); } #if 0 //判断当前是否处于中断模式 //根据局部变量地址进行判断, UCOS中是在所有的中断函数中置一个变量,表示当前为中断模式,退出时清楚标志 //也可以搞一个系统全局变量,所有中断函数入口置位,返回时清除.这样速度快,但编写中断函数时需要额外操作 static uint32 __inline func_is_in_isr(void) { //extern uint32 __heap_base; //堆首地址 extern uint32 __heap_limit; //堆末地址 也是中断栈的首地址 //extern uint32 __initial_sp; //中断栈末地址 //#define OS_ISR_TASK_SIZE (0X400) //当前系统栈地址为0x400(1kbyte) //#define OS_ISR_TASK_SIZE (__initial_sp - __heap_limit) //当前系统保留栈大小,具体配置在startup*.s中 uint32 temp = 0; if(((uint32)&temp)>((uint32)&__heap_limit)) return 1; else return 0; } #endif /***************************************************************************** 函 数 名 : create_task 功能描述 : 创建任务API函数 输入参数 : void (*fun)(void*) 任务函数主体 char* pcname 任务名称 uint32 task_id 任务ID,全局唯一 uint32 task_pro 任务优先级 uint32 stacksize 堆栈大小,若无特殊需要,使用默认值即可 注意单位为4字节(机器字长) uint32 queuesize 消息队列大小,大小需是8(MSG大小)的整倍数 注意单位为4字节 输出参数 : 返 回 值 : 0 成功 其他:异常 调用函数 : 被调函数 : 修改历史 : 1.日 期 : 2012年12月24日 作 者 : 郭金涛 修改内容 : 新生成函数 *****************************************************************************/ uint32 create_task(void (*fun)(void*),char* pcname,uint32 task_id,uint32 task_pro,uint32 stacksize,uint32 queuesize) { uint32 ret_value = 0; pSYS_TASK ptask = &sys_task; //为输出参数, uint32 task_tcb = 0; if(task_id>MAX_TASK) return ERR_TASKID_TOO_LARGE; ret_value = xTaskCreate(fun,(const char*)pcname,stacksize,NULL,task_pro,(void*)&task_tcb); if(ret_value!=pdPASS) return ERR_CREATE_TASK_FAILED; vTaskSetTaskNumber((void*)task_tcb,task_id); if(queuesize) { ptask->queue[task_id] = (void*)xQueueGenericCreate( queuesize/sizeof(void*), sizeof(void*), queueQUEUE_TYPE_BASE); if(ptask->queue==NULL) return ERR_CREATE_QUEUE_FAILED; } ptask->created[task_id] = 1; ptask->num++; return 0; } #if 1//_USE_RVL_ //将task_id的任务配置为进制任务状态检查 uint32 task_set_ignore_chk(uint32 task_id) { if(task_id>MAX_TASK) return ERR_TASKID_TOO_LARGE; sys_task.ignore_chk[task_id] = 1; return 0; } /***************************************************************************** 函 数 名 : send_msg 功能描述 : 向对应id的任务发送消息 RTOS函数封装,是否需要判断当前是否处于中断状态 输入参数 : uint32 task_id 目的任务ID(内部仅用到高16位) pMSG pmsg 消息体指针 输出参数 : 无 返 回 值 :uint32 0 成功, 其他 异常 调用函数 : 被调函数 : 修改历史 : 1.日 期 : 2012年12月24日 作 者 : 郭金涛 修改内容 : 新生成函数 signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) 实现对上述两个函数的封装 且采用FIFO模式 *****************************************************************************/ uint32 send_msg(uint32 task_id,pMSG pmsg) { uint32 ret = 0; #if USE_MSG_DEBUG MSG_DEBUG *pdebug_msg = (MSG_DEBUG *)pmsg; pdebug_msg->rec_id = task_id; #endif if(task_id>MAX_TASK) return ERR_SEND_MSG_FAILED; if(!sys_task.queue[task_id]) return ERR_SEND_MSG_FAILED; //if(func_is_in_isr()) //是否处于中断处理函数中 可以定义个全局变量. if(os_int_nesting) { int32 need_sched = pdFALSE; ret = xQueueGenericSendFromISR(sys_task.queue[task_id],(const void*)&pmsg,(BaseType_t *)&need_sched,queueSEND_TO_BACK); if(ret!=pdPASS) return ERR_SEND_MSG_FAILED; if(need_sched) //若需要任务立即调度,调度此函数,以配置pendsv中断 taskYIELD(); taskENTER_CRITICAL(); msg_state.isr_send_num++; taskEXIT_CRITICAL(); } else { ret = xQueueGenericSend(sys_task.queue[task_id],(const void*)&pmsg,0,queueSEND_TO_BACK); if(ret != pdPASS) return ERR_SEND_MSG_FAILED; taskENTER_CRITICAL(); msg_state.task_send_num++; taskEXIT_CRITICAL(); } return 0; } /***************************************************************************** 函 数 名 : get_msg 功能描述 : 接收该任务收到的消息 RTOS底层函数封装,接收消息由于当前结构原因,不需要在中断中接收消息,不需考虑 输入参数 : uint32 wait_ticks 等待的tick值,若为(-1),则为一直等待 输出参数 : 无 返 回 值 : 调用函数 : 被调函数 : 修改历史 : 1.日 期 : 2012年12月24日 作 者 : 郭金涛 修改内容 : 新生成函数 signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) 对上述函数的封装 *****************************************************************************/ pMSG get_msg(uint32 wait_ticks) { MSG *pmsg = NULL; uint32 task_id; get_current_task_id(&task_id); if(task_id>=MAX_TASK) return NULL; if(sys_task.queue[task_id]==NULL) return NULL; //xQueueGenericReceive(sys_task.queue[task_id],(void*)&pmsg,wait_ticks,pdFALSE); xQueueReceive(sys_task.queue[task_id],(void*)&pmsg,wait_ticks);//_USE_RVL_ //Protype:BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) return pmsg; } //可考虑增加一些调试信息 //最好不要再中断函数中调用, void *alloc_mem(uint32 size) { MEM_PTR *pmem = NULL; //申请内存4字节对齐. 若未对齐,增加填充字节 if(size&3) { size += 4-(size&3); } #if USE_MEM_DEBUG pmem = pvPortMalloc(size+sizeof(MEM_DEBUG_HEADER)); #else pmem = pvPortMalloc(size); #endif if(!pmem) { return NULL; } #if USE_MEM_DEBUG pmem->header.size = size; pmem->header.tick = get_sys_tick(); taskENTER_CRITICAL(); mem_state.used+=size; taskEXIT_CRITICAL(); #endif return (void*)&pmem->mem[0]; } void free_mem(void* p) { MEM_PTR *pmem = (MEM_PTR *)p; if(!pmem) return; #if USE_MEM_DEBUG pmem = (MEM_PTR *)((uint32)pmem-sizeof(MEM_DEBUG_HEADER)); taskENTER_CRITICAL(); mem_state.used-=pmem->header.size; taskEXIT_CRITICAL(); #endif vPortFree(pmem); } /***************************************************************************** 函 数 名 : alloc_msg 功能描述 : 申请消息函数,FREERTOS不同于UCOS,实际上消息只是个内存区,也可以调用alloc_mem来自由实现,甚至申请一块很大的内存发送消息 输入参数 : void 输出参数 : 无 返 回 值 : 调用函数 : 被调函数 : 修改历史 : 1.日 期 : 2013年5月3日 作 者 : 郭金涛 修改内容 : 新生成函数 调用此函数申请的消息,是一个MSG类型的结构体指针 *****************************************************************************/ MSG* alloc_msg(void) { #if USE_MSG_DEBUG//打开后,需要改下代码 MSG_DEBUG *pdebug_msg = NULL; pdebug_msg = alloc_mem(sizeof(MSG_DEBUG)); if(pdebug_msg==NULL) return NULL; if(os_int_nesting) { pdebug_msg->alloc_id = OS_MSG_ISR_TASK_ID; taskENTER_CRITICAL(); msg_state.isr_alloc_num++; taskEXIT_CRITICAL(); } else { pdebug_msg->alloc_id = uxTaskPriorityGet(NULL); taskENTER_CRITICAL(); msg_state.task_alloc_num++; taskEXIT_CRITICAL(); } pdebug_msg->magci_word = OS_MSG_MAGIC_WORD; //pdebug_msg->need_free = MSG_NEED_FREE; return &pdebug_msg->msg; #else pMSG pmsg = NULL; pmsg = alloc_mem(sizeof(MSG)); if(pmsg==NULL) return NULL; pmsg->type = 0; pmsg->wPara = 0; pmsg->rPara = 0; pmsg->need_free = MSG_NEED_FREE; pmsg->lPara = 0; if(os_int_nesting) { taskENTER_CRITICAL(); msg_state.isr_alloc_num++; taskEXIT_CRITICAL(); } else { taskENTER_CRITICAL(); msg_state.task_alloc_num++; taskEXIT_CRITICAL(); } return pmsg; #endif } /***************************************************************************** 函 数 名 : free_msg 功能描述 : 释放消息函数,理论上该函数要与alloc_msg配对使用. 处理后的消息,是否需要释放,要有用户判断 //当前不需要释放的有sec,min,timer和key消息 输入参数 : pMSG p 输出参数 : 无 返 回 值 : 调用函数 : 被调函数 : 修改历史 : 1.日 期 : 2013年5月3日 作 者 : 郭金涛 修改内容 : 新生成函数 *****************************************************************************/ void free_msg(MSG *p) { #if USE_MSG_DEBUG MSG_DEBUG *pdebug_msg = (MSG_DEBUG *)p; if(pdebug_msg->magci_word!= OS_MSG_MAGIC_WORD) { ; //内存使用可能存在异常 } free_mem(pdebug_msg); #else free_mem(p); #endif taskENTER_CRITICAL(); msg_state.free_num++; taskEXIT_CRITICAL(); } SEM_HANDLE create_sem(uint32 max_count,uint32 init_count) { return xQueueCreateCountingSemaphore(max_count,init_count); } //等待信号量 会挂起任务 uint32 pend_sem(SEM_HANDLE sem_hd,uint32 wait_ticks) { uint32 ret = 0; if(NULL== sem_hd) return ERR_SEM_PTR_INVALID; ret = xSemaphoreTake(sem_hd,wait_ticks); if(ret!=pdPASS) return ERR_PEND_SEM_FAILED; return 0; } //发送信号量 uint32 post_sem(SEM_HANDLE sem_hd) { uint32 ret = 0; if(sem_hd==NULL) return ERR_SEM_PTR_INVALID; //if(func_is_in_isr()) //是否处于中断处理函数中 if(os_int_nesting) { int32 task_sched = pdFALSE; ret = xSemaphoreGiveFromISR(sem_hd,(BaseType_t *)&task_sched); if(ret!=pdPASS) return ERR_POST_SEM_FAILED; if(task_sched) taskYIELD(); return 0; } else { ret = xSemaphoreGive(sem_hd); if(ret!=pdPASS) return ERR_POST_SEM_FAILED; else return 0; } } MUTEX_HANDLE create_mutex(void) { return xSemaphoreCreateMutex(); } uint32 take_mutex(MUTEX_HANDLE mtx_hd) { uint32 ret = 0; if(sys_ready) { if(NULL==mtx_hd) return ERR_MUTEX_PTR_INVALID; ret = xSemaphoreTake(mtx_hd,OS_WAIT_FOREVER); if(ret!=pdPASS) return ERR_TAKE_MUTEX_FAILED; } return 0; } uint32 give_mutex(MUTEX_HANDLE mtx_hd) { uint32 ret = 0; if(sys_ready) { if(NULL==mtx_hd) return ERR_MUTEX_PTR_INVALID; ret = xSemaphoreGive(mtx_hd); if(ret!=pdPASS) return ERR_GIVE_MUTEX_FAILED; else return 0; } return 0; } /* void task_stop_sec_msg(void) { uint32 task_id = 0; if(!sys_ready) return; get_current_task_id(&task_id); if(task_id>=MAX_TASK) { return; } sys_task.pause[task_id] = 0XFF; } void task_resume_sec_msg(void) { uint32 task_id = 0; if(!sys_ready) return; get_current_task_id(&task_id); if(task_id>=MAX_TASK) { return; } sys_task.pause[task_id] = 0; }*/ #endif//_USE_RVL_ /***********计算CPU利用率相关函数***********************/ #if 1//_USE_RVL_ #define AVERAGE_NUM 4 #define AVERAGE() \ do { \ uint32 i,j; \ for(i=0,j=0;i100) aCpu[index] = 0; AVERAGE(); index++; if(index >= AVERAGE_NUM) { index = 0; } } }*/ void calc_cpu_usage(void) { #if configUSE_CALC_CPU_USAGE static const uint32 sec_run_time = 5418758/100; //经验数值 uint32 index = 0; static uint8 aCpu[AVERAGE_NUM] = {0,}; aCpu[index] = 100-get_idle_run_time()/sec_run_time; if(aCpu[index]>100) aCpu[index] = 0; AVERAGE(); index++; if(index >= AVERAGE_NUM) { index = 0; } set_idle_run_time(0); #endif } uint32 get_cpu_useage(void) { return cpu_useage; } static void check_sec_tick(void) { static uint32 sec_tick = 0; if(!sys_ready) { return; //启动完成后再发,否则main_task会堆积过多的秒消息 } sec_tick++; if(sec_tick>=OS_TICKS_PER_SEC) { sec_tick = 0; send_msg(MAIN_TASK_ID,(pMSG)&sec_msg); } } static void send_min_msg(void) { uint32 i = 0;; while(i=60) { run_time.sec = 0; send_min_msg(); if(++run_time.min>=60) { run_time.min = 0; run_time.hour++; } } } uint32 get_current_task_id(uint32 *pid) { if(os_int_nesting) { *pid = (uint32)-1; return 1; } else { *pid = uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle()); return 0; } } uint32 get_task_id(uint32 *pid,void *ptcb) { if(os_int_nesting) { *pid = (uint32)-1; return 1; } else { *pid = uxTaskGetTaskNumber(ptcb); return 0; } } //配置当前任务能收到的时间消息,秒消息主任务不需要配置 uint32 config_time_msg(uint32 type) { uint32 task_id = 0; get_current_task_id(&task_id); if(task_id>=MAX_TASK) { return 2; } switch(type) { case MSG_SEC: time_msg_queue.sec_msg_q[time_msg_queue.sec_num++] = task_id; break; case MSG_MINUTE: time_msg_queue.min_msg_q[time_msg_queue.min_num++] = task_id; break; default: break; } return 0; } get_sys_time /* void get_sys_time(S_SYS_TIME *ptime) { if(!ptime) return; ptime->hour = date_time.hour; ptime->min = date_time.min; ptime->sec = date_time.sec; ptime->date = date_time.date; ptime->month= date_time.month; ptime->year = date_time.year; return; } */ S_SYS_TIME *os_get_sys_time_ptr(void) { return &date_time; } typedef struct { uint8_t WeekDay; /*!< Specifies the RTC Date WeekDay. This parameter can be a value of @ref RTC_WeekDay_Definitions */ uint8_t Month; /*!< Specifies the RTC Date Month (in BCD format). This parameter can be a value of @ref RTC_Month_Date_Definitions */ uint8_t Date; /*!< Specifies the RTC Date. This parameter must be a number between Min_Data = 1 and Max_Data = 31 */ uint8_t Year; /*!< Specifies the RTC Date Year. This parameter must be a number between Min_Data = 0 and Max_Data = 99 */ }usr_PKT_RTC_DateTypeDef; typedef struct { uint8_t Hours; /*!< Specifies the RTC Time Hour. This parameter must be a number between Min_Data = 0 and Max_Data = 12 if the RTC_HourFormat_12 is selected. This parameter must be a number between Min_Data = 0 and Max_Data = 23 if the RTC_HourFormat_24 is selected */ uint8_t Minutes; /*!< Specifies the RTC Time Minutes. This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ uint8_t Seconds; /*!< Specifies the RTC Time Seconds. This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ uint8_t TimeFormat; /*!< Specifies the RTC AM/PM Time. This parameter can be a value of @ref RTC_AM_PM_Definitions */ uint32_t SubSeconds; /*!< Specifies the RTC_SSR RTC Sub Second register content. This parameter corresponds to a time unit range between [0-1] Second with [1 Sec / SecondFraction +1] granularity */ uint32_t SecondFraction; /*!< Specifies the range or granularity of Sub Second register content corresponding to Synchronous pre-scaler factor value (PREDIV_S) This parameter corresponds to a time unit range between [0-1] Second with [1 Sec / SecondFraction +1] granularity. This field will be used only by HAL_RTC_GetTime function */ uint32_t DayLightSaving; /*!< Specifies DayLight Save Operation. This parameter can be a value of @ref RTC_DayLightSaving_Definitions */ uint32_t StoreOperation; /*!< Specifies RTC_StoreOperation value to be written in the BCK bit in CR register to store the operation. This parameter can be a value of @ref RTC_StoreOperation_Definitions */ }usr_PKT_RTC_TimeTypeDef; //设置系统时钟,更新本地,同时更改RTC时钟 void set_sys_time(S_SYS_TIME *p) { RTC_DateTypeDef date; RTC_TimeTypeDef time; S_SYS_TIME tmptime={0}; if(!p) { return; } date.RTC_Year = p->year-2000; date.RTC_Date = p->date; date.RTC_Month= p->month; time.RTC_Hours = p->hour; time.RTC_Minutes= p->min; time.RTC_Seconds= p->sec; rtc_set_date_time(&date,&time); tmptime.year = date.RTC_Year; tmptime.month = date.RTC_Month; tmptime.date = date.RTC_Date; tmptime.hour = time.RTC_Hours; tmptime.min = time.RTC_Minutes; tmptime.sec = time.RTC_Seconds; usr_set_sys_rtc_time(&tmptime); } void update_sys_time_by_rtc(void) { RTC_DateTypeDef date; RTC_TimeTypeDef time; //printf("%s ..\n",__FUNCTION__); rtc_get_date_time(&date,&time); portENTER_CRITICAL(); date_time.year = date.RTC_Year+2000; date_time.date = date.RTC_Date; date_time.month= date.RTC_Month; date_time.hour = time.RTC_Hours; //默认为24小时模式 date_time.min = time.RTC_Minutes; date_time.sec = time.RTC_Seconds; portEXIT_CRITICAL(); //printf("System time:%04d-%02d-%02d %02d:%02d:%02d.\n", // date_time.year,date_time.month,date_time.date,date_time.hour,date_time.min,date_time.sec); //printf("%s success.\n",__FUNCTION__); } void rtc_update_by_gps(GPRMC_T *p) { portENTER_CRITICAL(); date_time.year = p->year; date_time.date = p->day; date_time.month= p->month; date_time.hour = p->hour; //默认为24小时模式 date_time.min = p->min; date_time.sec = p->sec; date_time.valid = 1; portEXIT_CRITICAL(); set_sys_time(&date_time); } void get_msg_state(S_MSG_STATE *pstate) { if(!pstate) { return; } taskENTER_CRITICAL(); memcpy(pstate,&msg_state,sizeof(msg_state)); taskEXIT_CRITICAL(); } void get_mem_state(S_MEM_STATE *pstate) { if(!pstate) return; #if USE_MEM_DEBUG taskENTER_CRITICAL(); memcpy(pstate,&mem_state,sizeof(mem_state)); taskEXIT_CRITICAL(); #else pstate->total = (uint32)-1; pstate->used = (uint32)-1; #endif } uint32 os_get_free_heap_size(void) { return xPortGetFreeHeapSize(); } HANDLE create_timer(void) { uint32 i; uint32 task_id = 0; for(i=0;i=MAX_TASK) return (HANDLE)-1; tick_timer[i].task_id = task_id; tick_timer[i].state = TMR_STOP; tick_timer[i].msg.type = MSG_TIMER; tick_timer[i].msg.lPara = (HANDLE)i; return i; } } return (HANDLE)-1; } /* hdl: 定时器句柄,创建时获取 interval: 定时器间隔,单位tick type: TMR_TYPE_ONE 单次定时器 TMR_TYPE_CYCLE 周期性定时器 */ void start_timer(HANDLE hdl,uint32 interval_ticks,uint32 type) { if(tick_timer[hdl].state == TMR_IDLE) return; tick_timer[hdl].ticks = interval_ticks; tick_timer[hdl].count = tick_timer[hdl].ticks; tick_timer[hdl].state = TMR_RUN; tick_timer[hdl].type = type; } void stop_timer(HANDLE hdl) { tick_timer[hdl].state = TMR_STOP; } void delete_timer(HANDLE hdl) { tick_timer[hdl].state = TMR_IDLE; } //由tick中断调用 static void timer_check(void) { uint32 i; TIMER *p = &tick_timer[0]; if(!sys_ready) return; for(i=0;istate == TMR_RUN) { if(!--p->count) { send_msg(p->task_id,&p->msg); //处于暂停状态的是否发送? if(p->type == TMR_TYPE_CYCLE) p->count = p->ticks; //周期性定时器,需再次载入周期值 else p->state = TMR_STOP; } } } } //由系统tick中断维护时钟变化,1分钟与RTC同步一次 static void update_sys_time(void) { static uint32 sec_tick = 0; if(!sys_ready) return; sec_tick++; if(sec_tickOS_TASK_MAX_ABNORMAL_TIME) { return i; } } return 0; //主任务id为0 } //检测到异常任务,可写日志信息等.具体待扩展 void os_dead_task_abnormal(uint32 task_id) { #if USE_DEBUG_UART char buf[32]; #endif printf(buf,"Abnormal task id:%d!",task_id); } //WWDG看门狗中断触发,可以保存临终遗言,日志等信息 void os_dead_wwdg(void) { #if USE_DEBUG_UART char buf[32]; #endif printf(buf,"Abnormal wwdg!"); } #endif//_USE_RVL_ /**********************************************END***************************************************/