【读书笔记】usOS-II——互斥型信号量(mutex)

本文详细介绍了互斥信号量的功能及其在解决实时系统中的优先级反转问题中的作用。通过具体的代码解析,展示了互斥信号量如何实现资源的独占访问,并通过优先级继承机制来避免优先级反转现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/yhmhappy2006/article/details/3357335

互斥型信号量(mutex)

互斥信号量功能:

1)  实现对资源的独占式访问(二值信号量)。

2)  降解优先级反转。

优先级反转:

使用实时内核,优先级反转问题是实时系统中出现得最多的问题。设,任务1优先级高于任务2,任务2优先级高于任务3。任务1和任务2处于挂起状态,等待某一事件的发生,任务3正在运行如[图2.7(1)]。此时,任务3要使用其共享资源。使用共享资源之前,首先必须得到该资源的信号量(Semaphore)(见2. 18.04信号量)。任务3得到了该信号量,并开始使用该共享资源[图2.7(2)]。由于任务1优先级高,它等待的事件到来之后剥夺了任务3的CPU使用权[图2.7(3)],任务1开始运行[图2.7(4)]。运行过程中任务1也要使用那个任务3正在使用着的资源,由于该资源的信号量还被任务3占用着,任务1只能进入挂起状态,等待任务3释放该信号量[图2.7(5)]。任务3得以继续运行[图2.7(6)]。由于任务2的优先级高于任务3,当任务2等待的事件发生后,任务2剥夺了任务3的CPU的使用权[图2.7(7)]并开始运行。处理它该处理的事件[图2.7(8)],直到处理完之后将CPU控制权还给任3[图2.7(9)]。任务3接着运行[图2.7(10)],直到释放那个共享资源的信号量[图27(11)]。直到此时,由于实时内核知道有个高优先级的任务在等待这个信号量,内核做任务切换,使任务1得到该信号量并接着运行[图2.7(12)]。

在这种情况下,任务1优先级实际上降到了任务3 的优先级水平。因为任务1要等,直等到任务3释放占有的那个共享资源。由于任务2剥夺任务3的CPU使用权,使任务1的状况更加恶化,任务2使任务1增加了额外的延迟时间。任务1和任务2的优先级发生了反转。

纠正的方法可以是,在任务3使用共享资源时,提升任务3的优先级。任务完成时予以恢复。任务3的优先级必须升至最高,高于允许使用该资源的任何任务。多任务内核应允许动态改变任务的优先级以避免发生优先级反转现象。然而改变任务的优先级是很花时间的。如果任务3并没有先被任务1剥夺CPU使用权,又被任务2抢走了CPU使用权,花很多时间在共享资源使用前提升任务3的优先级,然后又在资源使用后花时间恢复任务3的优先级,则无形中浪费了很多CPU时间。真正需要的是,为防止发生优先级反转,内核能自动变换任务的优先级,这叫做优先级继承(Priority inheritance)。

互斥信号量降解优先级反转的过程:

设mutex已被低优先级的任务3占用。高优先级的任务1提出申请mutex(调用Pend())。在这种情况下:

1)              Pend函数注意到高优先级的任务要用这个共享资源,于是将任务3的优先级升高至9(创建mutex时指定,比任何提出申请mutex的任务的优先级都要高),并强制任务调度(由于任务3的优先级升高至9,因此任务3执行),任务3继续使用共享资源。当共享资源使用完后,任务3调用Post函数,释放mutex。

2)              Post函数注意到原来占用这个mutex的任务的优先级是被太高的,于是将任务3的优先级恢复到原来水平。

3)              Post还注意到有个高优先级的的任务(任务1)正在等待这个mutex,于是将mutex交给这个任务,并做任务切换,让任务1运行。

互斥信号量的组成:

l         一个标志,指示mutex是否可用(OS_MUTEX_AVAILABLE表示可用)。

l         一个优先级,即优先级继承优先级(PIP)。

l         一个等待mutex的任务列表。

创建一个互斥信号量:OSMutexCreate()

互斥信号量创建初的几个成员的值:

OS_EVENT

OSEventType

OS_EVENT_TYPE_MUTEX

OSEventCnt

PIP

OS_MUTEX_AVAILABLE

OSEventPtr

NULL

OSEventGrp

0

OSEventTbl

0

PIP是该函数的参数,指定优先级继承优先级。当发生优先级反转时,将占用该mutex的任务的优先级太高到PIP。

等待(申请)一个互斥信号量:OSMutexPend()

关键代码剖析:

void  OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)

{

   //不得在中断中调用该函数

    if (OSIntNesting > 0) {                               

        *err = OS_ERR_PEND_ISR;                           

        return;

}

OS_ENTER_CRITICAL();   

 

//信号量可用                                             

if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8)

                               == OS_MUTEX_AVAILABLE) {

        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;     

        //将计数器低8为置成占用该mutex的任务(当前任务)的优先级。

        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;

        //mutex中保存占用信号量的任务:修改该mutexOSEventPtr ,使其指向当前任务

        pevent->OSEventPtr  = (void *)OSTCBCur;           

        OS_EXIT_CRITICAL();

        //信号量可用,正常返回。

        *err  = OS_NO_ERR;

        return;

}

//信号量不可用:即已被占用

//从该信号量中获得PIP

pip   = (INT8U)(pevent->OSEventCnt >> 8);

//从该信号量中获得占用该信号量的任务的优先级。                   

mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 

//从信号量中获得占用该信号量的任务

ptcb  = (OS_TCB *)(pevent->OSEventPtr); 

 

/*

     如果原先占用该mutex的优先级比提出申请该mutex的任务的优先级低

    mprio > OSTCBCur->OSTCBPrio),则提升原任务的优先级至PIP

*/

    if (ptcb->OSTCBPrio != pip && mprio > OSTCBCur->OSTCBPrio) { 

        if ((OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) != 0x00) {    

            if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0x00) {

                OSRdyGrp &= ~ptcb->OSTCBBitY;

            }

            //若原任务已就绪,则将其从就绪表中删除,并置就绪标志rdy

            rdy = TRUE;

        } else {

            rdy = FALSE;                                         

        }

        //修改优先级,及相关参数

        ptcb->OSTCBPrio  = pip;                     

        ptcb->OSTCBY    = ptcb->OSTCBPrio >> 3;

        ptcb->OSTCBBitY  = OSMapTbl[ptcb->OSTCBY];

        ptcb->OSTCBX     = ptcb->OSTCBPrio & 0x07;

        ptcb->OSTCBBitX   = OSMapTbl[ptcb->OSTCBX];

 

        //如果原任务是就绪的,则继续让新的优先级就绪

        if (rdy == TRUE) {                               

            OSRdyGrp               |= ptcb->OSTCBBitY;   

            OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

        }

        OSTCBPrioTbl[pip]   = (OS_TCB *)ptcb;

}

//让提出申请的任务先等待(从就绪表中删除,如mutex的等待队列)………

    OSTCBCur->OSTCBStat |= OS_STAT_MUTEX;           

    OSTCBCur->OSTCBDly   = timeout;                  

    OS_EventTaskWait(pevent); 

OS_EXIT_CRITICAL();

//执行任务切换(如果原来低优先级的任务优先级被抬高了,则该任务将被执行)

    OS_Sched();                                       

OS_ENTER_CRITICAL();

//提出申请的任务被唤醒继续执行

if (OSTCBCur->OSTCBStat & OS_STAT_MUTEX) {

    //1)由于等待超时被定时器唤醒       

        OS_EventTO(pevent);

        OS_EXIT_CRITICAL();

        *err = OS_TIMEOUT;                           

        return;

}

/*

      2)原先占用mutex的任务执行完成释放了mutex

         并唤醒了等待该mutex的最高优先级的任务

*/

    OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;

    OS_EXIT_CRITICAL();

    *err = OS_NO_ERR;

}

 

释放一个互斥信号量:OSMutexPost()

关键代码剖析:

INT8U  OSMutexPost (OS_EVENT *pevent)

{

   //不得在中断中调用该函数

    if (OSIntNesting > 0) {                          

        return (OS_ERR_POST_ISR);                   

    }

OS_ENTER_CRITICAL();

 

//从该信号量中获得PIP

pip  = (INT8U)(pevent->OSEventCnt >> 8);         

/*

    从该信号量中获得占用该信号量的任务的优先级。

    OSEventCnt中的低8位保存占用mutex的任务的原始优先级

    不随优先级的提高而改变。

*/

prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 

/*

    确认释放mutex的任务确实是占用mutex的任务自身。

    占用/申请mutex的任务的优先级可能是pip(被提高),也可能是原先任务的优先级。

*/

    if (OSTCBCur->OSTCBPrio != pip &&

        OSTCBCur->OSTCBPrio != prio) {              

        OS_EXIT_CRITICAL();

        //若释放mutex的任务非占用/申请的任务,则返回错误信息。

        return (OS_ERR_NOT_MUTEX_OWNER);

}

   //若当前释放mutex的任务的优先级为pip,则需将该任务的优先级降到原来水平

if (OSTCBCur->OSTCBPrio == pip) {

   //首先将pip从就绪表删除

        if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) {

            OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

        }

        //将任务优先级修改为原始优先级,并修改相关参数

        OSTCBCur->OSTCBPrio   = prio;

        OSTCBCur->OSTCBY     = prio >> 3;

        OSTCBCur->OSTCBBitY   = OSMapTbl[OSTCBCur->OSTCBY];

        OSTCBCur->OSTCBX      = prio & 0x07;

        OSTCBCur->OSTCBBitX    = OSMapTbl[OSTCBCur->OSTCBX];

        //将修改优先级后的任务重新如就绪表

        OSRdyGrp           |= OSTCBCur->OSTCBBitY;

        OSRdyTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;

        OSTCBPrioTbl[prio]    = (OS_TCB *)OSTCBCur;

    }

OSTCBPrioTbl[pip] = (OS_TCB *)1;                 

//mutex的等待列表不空,唤醒等待列表中最高优先级的任务,并将mutex分配给它

if (pevent->OSEventGrp != 0x00) {          

    /*

        唤醒等待列表中最高优先级的任务(从mutex等待列表中删除,使其入就绪表),

        清除等待任务的OS_STAT_MUTEX标志,并返回其优先级prio

    */

        prio  = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX);

        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; 

        //mutex分配给新任务:置OSEventCntprio

        pevent->OSEventCnt |= prio;

        //mutex中保存占用信号量的任务

        pevent->OSEventPtr  = OSTCBPrioTbl[prio];    

        OS_EXIT_CRITICAL();

        //任务切换(如果唤醒的任务优先级比当前任务高,则使唤醒的任务得到运行)

        OS_Sched();                                  

        return (OS_NO_ERR);

}

//mutex的等待列表为空,即该mutex可用:置mutex可用标志及占用任务指针。

    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;       

    pevent->OSEventPtr  = (void *)0;

    OS_EXIT_CRITICAL();

    return (OS_NO_ERR);

}

 


/* USER CODE BEGIN Header */ /** ****************************************************************************** * File Name : freertos.c * Description : Code for freertos applications ****************************************************************************** * @attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "cmsis_os.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "usart.h" #include "gpio.h" #include "tim.h" #include <string.h> #include <stdio.h> /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define MOTOR_PWM_MAX 19999 // PWM最大值 (Period - 1) #define MOTOR_PWM_MIN 0 // PWM最小值 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN Variables */ struct uart3_rx_t { int num,ok,en; uint8_t data[28]; int class; float score; int x1,y1,x2,y2; uint8_t datarx; }uart3_rx; struct uart2_rx_t { int num,ok,en; uint8_t data[3]; int shexiangtou_en; int manzaijianche_en; int dangban_en; int youhailaji_en; int chuyulaji_en; int kehuishou_en; int qitalaji_en; uint8_t datarx; }uart2_rx; struct pingmu_tx_t { int manzai,ok,en; int class; float score; int x1,y1,x2,y2; }pingmu_tx; osThreadId motorTaskHandle; osThreadId motor2TaskHandle; osThreadId manzaiTaskHandle; osThreadId txTaskHandle; osThreadId uart6MutexHandle; // 新增:电机控制和超声波测距任务句柄 osThreadId motorControlTaskHandle; // 电机控制任务句柄 osThreadId ultrasonicTaskHandle; // 超声波测距任务句柄 // 新增:超声波测距变量 float ultrasonic1_distance = 0.0f; // 超声波1测量距离 (cm) float ultrasonic2_distance = 0.0f; // 超声波2测量距离 (cm) // 新增:舵机状态跟踪变量 typedef enum { SERVO_IDLE = 0, // 舵机空闲状态(在原位) SERVO_WORKING = 1, // 舵机正在执行分类动作 SERVO_RETURNING = 2 // 舵机正在回到原位 } ServoState_t; volatile ServoState_t servoState = SERVO_IDLE; // 舵机状态 volatile uint32_t servoWorkStartTime = 0; // 舵机工作开始时间 // 新增:电机启动停止延时控制变量 volatile uint32_t motor1_startConditionStartTime = 0; // 电机1启动条件开始时间 volatile uint32_t motor2_startConditionStartTime = 0; // 电机2启动条件开始时间 volatile uint8_t motor1_startConditionActive = 0; // 电机1启动条件是否激活 volatile uint8_t motor2_startConditionActive = 0; // 电机2启动条件是否激活 volatile uint32_t motor1_stopConditionStartTime = 0; // 电机1停止条件开始时间 volatile uint32_t motor2_stopConditionStartTime = 0; // 电机2停止条件开始时间 volatile uint8_t motor1_stopConditionActive = 0; // 电机1停止条件是否激活 volatile uint8_t motor2_stopConditionActive = 0; // 电机2停止条件是否激活 // 电机当前状态 volatile uint8_t motor1_running = 0; // 电机1是否正在运行 volatile uint8_t motor2_running = 0; // 电机2是否正在运行 volatile uint8_t servo_class_to_act = 0; volatile uint8_t is_playing_manzai = 0; // 满载播报进行中标志 volatile uint32_t manzai_play_start = 0; // 满载播报开始时间 volatile uint32_t garbage_delay_start = 0; // 垃圾识别延迟开始时间 volatile uint8_t garbage_to_play = 0; // 待播放的垃圾类别 // ✅ 新增电机使能状态 typedef enum { MOTOR_ENABLED = 0, MOTOR_DISABLED = 1 } MotorState_t; volatile MotorState_t motorState = MOTOR_ENABLED; // 默认启用 /* USER CODE END Variables */ osThreadId defaultTaskHandle; osSemaphoreId motorHandle; osSemaphoreId motor2Handle; osSemaphoreId rxlubancatHandle; osSemaphoreId rxpingmuHandle; osSemaphoreId bujingdianjiHandle; /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN FunctionPrototypes */ int fun (int a,int b, int c); //_Noreturn void systemMonitorTask(void const * argument); _Noreturn void motor2Task(void const * argument); //_Noreturn void bujingdianjiTask(void const * argument); _Noreturn void manzaiTask(void const * argument); _Noreturn void txTask(void const * argument); // 新增:电机控制和超声波测距任务函数声明 _Noreturn void motorControlTask(void const * argument); // 核心电机控制任务 _Noreturn void ultrasonicTask(void const * argument); // 超声波测距任务 /* USER CODE END FunctionPrototypes */ void StartDefaultTask(void const * argument); void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */ /* GetIdleTaskMemory prototype (linked to static allocation support) */ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xIdleTaskTCBBuffer; static StackType_t xIdleStack[configMINIMAL_STACK_SIZE]; void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; *ppxIdleTaskStackBuffer = &xIdleStack[0]; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* place for user code */ } /* USER CODE END GET_IDLE_TASK_MEMORY */ /** * @brief FreeRTOS initialization * @param None * @retval None */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* USER CODE BEGIN RTOS_MUTEX */ osMutexDef(uartMutex); osMutexId uartMutexHandle = osMutexCreate(osMutex(uartMutex)); osMutexDef(uart6Mutex); uart6MutexHandle = osMutexCreate(osMutex(uart6Mutex)); /* USER CODE END RTOS_MUTEX */ /* Create the semaphores(s) */ /* definition and creation of motor */ osSemaphoreDef(motor); motorHandle = osSemaphoreCreate(osSemaphore(motor), 1); /* definition and creation of motor2 */ osSemaphoreDef(motor2); motor2Handle = osSemaphoreCreate(osSemaphore(motor2), 1); /* definition and creation of rxlubancat */ osSemaphoreDef(rxlubancat); rxlubancatHandle = osSemaphoreCreate(osSemaphore(rxlubancat), 1); /* definition and creation of rxpingmu */ osSemaphoreDef(rxpingmu); rxpingmuHandle = osSemaphoreCreate(osSemaphore(rxpingmu), 1); /* definition and creation of bujingdianji */ osSemaphoreDef(bujingdianji); bujingdianjiHandle = osSemaphoreCreate(osSemaphore(bujingdianji), 1); /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ /* Create the thread(s) */ /* definition and creation of defaultTask */ osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128); defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ // osThreadDef(motorTask, motorTask, osPriorityNormal, 0, 128); // 添加舵机初始化 //__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 1300); // 舵机1初始位置 //__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 2030); // 舵机2初始位置 // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); // 启动PWM输出 // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); // servoState = SERVO_IDLE; // 设置初始状态 // motorTaskHandle = osThreadCreate(osThread(motorTask), NULL); // 保留现有的舵机控制任务 osThreadDef(motor2Task, motor2Task, osPriorityNormal, 0, 128); motor2TaskHandle = osThreadCreate(osThread(motor2Task), NULL); // 保留现有的满载检测任务 osThreadDef(manzaiTask, manzaiTask, osPriorityNormal, 0, 128); manzaiTaskHandle = osThreadCreate(osThread(manzaiTask), NULL); // osThreadDef(systemMonitorTask, systemMonitorTask, osPriorityBelowNormal, 0, 128); // osThreadCreate(osThread(systemMonitorTask), NULL); // 保留现有的串口屏通讯任务 osThreadDef(txTask, txTask, osPriorityNormal, 2, 128); txTaskHandle = osThreadCreate(osThread(txTask), NULL); // 新增:电机控制任务 osThreadDef(motorControlTask, motorControlTask, osPriorityNormal, 0, 256); motorControlTaskHandle = osThreadCreate(osThread(motorControlTask), NULL); // 新增:超声波测距任务 osThreadDef(ultrasonicTask, ultrasonicTask, osPriorityHigh, 0, 256); ultrasonicTaskHandle = osThreadCreate(osThread(ultrasonicTask), NULL); /* USER CODE END RTOS_THREADS */ } /* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void const * argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ uint32_t last_activity_time = HAL_GetTick(); uart2_rx.manzaijianche_en = 1; for(;;) { //xSemaphoreTake(rxpingmuHandle,portMAX_DELAY); if(xSemaphoreTake(rxpingmuHandle, 100) == pdTRUE) { switch (uart2_rx.data[1]) { case 1: uart2_rx.shexiangtou_en=0; break; case 0: uart2_rx.shexiangtou_en=1; break; case 3: uart2_rx.manzaijianche_en=0; break; case 2: uart2_rx.manzaijianche_en=1; break; case 4: uart2_rx.dangban_en=0; break; case 5: uart2_rx.dangban_en=1; break; case 6: uart2_rx.youhailaji_en=0; break; case 7: uart2_rx.youhailaji_en=1; break; case 8: uart2_rx.chuyulaji_en=0; break; case 9: uart2_rx.chuyulaji_en=1; break; case 10: uart2_rx.kehuishou_en=0; break; case 11: uart2_rx.kehuishou_en=1; break; case 12: uart2_rx.qitalaji_en=0; break; case 13: uart2_rx.qitalaji_en=1; break; default: break; } last_activity_time = HAL_GetTick(); } if(HAL_GetTick() - last_activity_time > 10000) { // 5秒无活动时软复位 NVIC_SystemReset(); } } /* USER CODE END StartDefaultTask */ } /* Private application code --------------------------------------------------*/ /* USER CODE BEGIN Application */ void PlayAudio(uint8_t audio_num) { char cmd[] = {0x70,0x6C,0x61,0x79,0x20,0x30,0x2C,0x30,0x2C,0x30, 0xFF,0xFF,0xFF}; cmd[7] = '0' + audio_num; osMutexWait(uart6MutexHandle, osWaitForever); HAL_UART_Transmit(&huart6, (uint8_t *)cmd, sizeof(cmd), HAL_MAX_DELAY); osMutexRelease(uart6MutexHandle); } void PlayGarbageAudio(uint8_t garbage_class) { uint8_t audio_num = 0; switch (garbage_class) { case 0: case 1: case 3: audio_num = 2; break; // 可回收 case 2: case 5: audio_num = 1; break; // 厨余 case 7: case 8: audio_num = 0; break; // 有害 default: audio_num = 3; break; // 其他 } PlayAudio(audio_num); } // 保留现有的舵机控制任务 - 增加状态跟踪 _Noreturn void motor2Task(void const * argument) { // 上电首次执行初始化动作 __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 1300); __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_2, 2030); osDelay(500); // 确保舵机到位 servoState = SERVO_IDLE; for(;;) { xSemaphoreTake(motor2Handle, portMAX_DELAY); //uint8_t current_class = uart3_rx.class; if(is_playing_manzai) { osDelay(100); continue; // 跳过当前动作 } // 🎯 设置舵机工作状态 servoState = SERVO_WORKING; //servoWorkStartTime = HAL_GetTick(); uint8_t current_class = servo_class_to_act; switch (current_class) { //有害垃圾 case 0: __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,1300); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,2430); break; //可回收垃圾 case 1: case 8: __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,1300); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,1570); break; //厨余垃圾 case 2: case 5: case 6: case 7: __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,2000); osDelay(1000); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,2430); break; //其它垃圾 case 3: case 4: __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,2000); osDelay(500); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,1570); break; default: __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,1300); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,2030); break; } osDelay(1000); // 执行分类动作的延时 // 🎯 设置舵机回到原位状态 servoState = SERVO_RETURNING; __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,1300); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,2030); osDelay(1000); // 回到原位的延时 // 🎯 设置舵机空闲状态 servoState = SERVO_IDLE; } osSemaphoreGive(motor2Handle); osDelay(10); } // 保留现有的满载检测任务 _Noreturn void manzaiTask(void const * argument) { for(;;) { osDelay(100); if(HAL_GPIO_ReadPin(load1_GPIO_Port,load1_Pin)==0 || HAL_GPIO_ReadPin(load2_GPIO_Port,load2_Pin)==0 || HAL_GPIO_ReadPin(load3_GPIO_Port,load3_Pin)==0 || HAL_GPIO_ReadPin(load4_GPIO_Port,load4_Pin)==0) { pingmu_tx.manzai=1; xSemaphoreGive(rxlubancatHandle); } } } // 保留现有的串口屏通讯任务 _Noreturn void txTask(void const * argument) { int num=0; const char manzaidata[]={0x74,0x30,0x2E,0x74,0x78,0x74,0x3D,0x22,0xC0,0xAC,0xBB, 0xF8,0xC2,0xFA,0xD4,0xD8,0x22,0xff,0xff,0xff}; // const char kongdata[]={0x74,0x30,0x2E,0x74,0x78,0x74,0x3D,0x22,0x20,0x22,0xff,0xff,0xff}; char play[]={0x70,0x6C,0x61,0x79,0x20,0x30,0x2C,0x30,0x2C,0x30}; unsigned char aa[2]={0}; const char start[]={0x64,0x61,0x74,0x61,0x30,0x2E ,0x69 ,0x6E ,0x73 ,0x65 ,0x72 ,0x74 ,0x28 ,0x22 }; const char end[]={0x22,0x29,0xff,0xff,0xff}; const char end2[]={0xff,0xff,0xff}; //���� const char data1[]={0x5E,0xCD,0xC1,0xB6,0xB9,'\0'}; //���ܲ� const char data2[]={0x5E ,0xB0 ,0xD7 ,0xC2 ,0xDC ,0xB2 ,0xB7 ,'\0'}; //���ܲ� const char data3[]={0x5E ,0xBA ,0xFA ,0xC2 ,0xDC ,0xB2 ,0xB7 ,'\0'}; //ֽ�� const char data4[]={0x5E ,0xD6 ,0xBD ,0xB1 ,0xAD ,'\0'}; //ʯͷ const char data5[]={0x5E ,0xCA ,0xAF ,0xCD ,0xB7 ,'\0'}; //��Ƭ const char data6[]={0x5E ,0xB4,0xC9 ,0xC6 ,0xAC ,'\0'}; //5�ŵ��????? const char data7[]={0x5E ,0x35 ,0xBA ,0xC5 ,0xB5 ,0xE7 ,0xB3 ,0xD8 ,'\0'}; //1�ŵ��????? const char data8[]={0x5E ,0x31 ,0xBA ,0xC5 ,0xB5 ,0xE7 ,0xB3 ,0xD8 ,'\0'}; //�к����� const char data10[]={0x5E ,0xD3 ,0xD0 ,0xBA ,0xA6 ,0xC0 ,0xAC ,0xBB ,0xF8 ,0x5E,'\0'}; //�������� const char data11[]={0x5E ,0xB3 ,0xF8 ,0xD3 ,0xE0 ,0xC0 ,0xAC ,0xBB ,0xF8 ,0x5E,'\0'}; //�ɻ������� const char data12[]={0x5E ,0xBF ,0xC9 ,0xBB ,0xD8 ,0xCA ,0xD5 ,0xC0 ,0xAC ,0xBB ,0xF8 ,0x5E,'\0'}; //�������� const char data13[]={0x5E ,0xC6 ,0xE4 ,0xCB ,0xFB ,0xC0 ,0xAC ,0xBB ,0xF8 ,0x5E,'\0'}; const char* data[]={data8,data4,data7,data5,data6,data2,data3,data1,data1,data1,data1}; uart2_rx.manzaijianche_en=0; uart2_rx.shexiangtou_en=1; for(;;) { xSemaphoreTake(rxlubancatHandle,portMAX_DELAY); if(uart3_rx.ok==1 && uart2_rx.shexiangtou_en ==1 ){ uart3_rx.ok=0; HAL_GPIO_TogglePin(led0_GPIO_Port,led0_Pin); uart3_rx.class=uart3_rx.data[1]-0x30; if(uart3_rx.class<0 || uart3_rx.class>9) uart3_rx.class=11; aa[1]=num%10+0x30; aa[0]=num/10+0x30; HAL_UART_Transmit(&huart6, (uint8_t *) start,sizeof(start),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); switch (uart3_rx.class) { case 0: case 2: HAL_UART_Transmit(&huart6, (uint8_t *) data[uart3_rx.class], strlen(data[uart3_rx.class]),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) data10, strlen(data10),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end, sizeof(end),0xFFFF); play[7]=0x30; HAL_UART_Transmit(&huart6, (uint8_t *) play, 10,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end2, sizeof(end2),0xFFFF); break;//�к����� case 5: case 6: case 7: HAL_UART_Transmit(&huart6, (uint8_t *) data[uart3_rx.class], strlen(data[uart3_rx.class]),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) data11, strlen(data11),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end, sizeof(end),0xFFFF); play[7]=0x31; HAL_UART_Transmit(&huart6, (uint8_t *) play, 10,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end2, sizeof(end2),0xFFFF); break;//�������� case 1: HAL_UART_Transmit(&huart6, (uint8_t *) data[uart3_rx.class], strlen(data[uart3_rx.class]),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) data12, strlen(data12),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end, sizeof(end),0xFFFF); play[7]=0x32; HAL_UART_Transmit(&huart6, (uint8_t *) play, 10,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end2, sizeof(end2),0xFFFF); break;//�ɻ������� case 3: case 4: HAL_UART_Transmit(&huart6, (uint8_t *) data[uart3_rx.class], strlen(data[uart3_rx.class]),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) data13, strlen(data13),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end, sizeof(end),0xFFFF); play[7]=0x33; HAL_UART_Transmit(&huart6, (uint8_t *) play, 10,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end2, sizeof(end2),0xFFFF); break;//�������� default: break; } servo_class_to_act = uart3_rx.class; xSemaphoreGive(motor2Handle); num++; if(num>99)num=0; osDelay(2000); } if(pingmu_tx.manzai==1 && uart2_rx.manzaijianche_en==1){ HAL_UART_Transmit(&huart6, (uint8_t *) manzaidata, 20,0xFFFF),pingmu_tx.manzai=0; play[7]=0x34; HAL_UART_Transmit(&huart6, (uint8_t *) play, 10,0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *) end2, sizeof(end2),0xFFFF); osDelay(2000); } } } // ========== 🎯 新增:电机控制任务 ========== // 功能:控制两个电机的PWM速度,与舵机控制分开 // 电机1:PA6 (TIM3_CH1) // 电机2:PA7 (TIM3_CH2) _Noreturn void motorControlTask(void const * argument) { uint32_t tickCounter = 0; // 延迟启动 osDelay(1000); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // PA6 HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); // PA7 for(;;) { // 电机现在由超声波任务自动控制,这里只做周期性检查 if(tickCounter % 50 == 0) { // 可以在这里添加电机状态检查逻辑 } tickCounter++; osDelay(200); } } // 添加DWT周期计数器支持(在main.c初始化) void DWT_Init(void) { if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } } // 微秒级延时函数 void DWT_Delay_us(volatile uint32_t us) { uint32_t start = DWT->CYCCNT; us *= (SystemCoreClock / 1000000); while ((DWT->CYCCNT - start) < us); } // ========== 🎯 新增:超声波测距函数 ========== // 简化版本,避免任务阻塞,返回距离值(cm*10) // 重写超声波测距函数 int32_t measureDistance(GPIO_TypeDef* trig_port, uint16_t trig_pin, GPIO_TypeDef* echo_port, uint16_t echo_pin) { // 发送10us触发脉冲 HAL_GPIO_WritePin(trig_port, trig_pin, GPIO_PIN_SET); DWT_Delay_us(10); HAL_GPIO_WritePin(trig_port, trig_pin, GPIO_PIN_RESET); uint32_t start_wait = DWT->CYCCNT; uint32_t timeout_ticks = 30 * (SystemCoreClock / 1000); //uint32_t timeout_cycles = DWT->CYCCNT + (25 * (SystemCoreClock / 1000)); // 25ms超时 while (HAL_GPIO_ReadPin(echo_port, echo_pin) == GPIO_PIN_RESET) { if ((DWT->CYCCNT - start_wait) > timeout_ticks) return -1; // 正确超时判断 } uint32_t echo_start = DWT->CYCCNT; //uint32_t start_time = DWT->CYCCNT; //timeout_cycles = DWT->CYCCNT + (25 * (SystemCoreClock / 1000)); // 25ms超时 //start = HAL_GetTick(); while (HAL_GPIO_ReadPin(echo_port, echo_pin) == GPIO_PIN_SET) { if ((DWT->CYCCNT - echo_start) > timeout_ticks) return -2; } uint32_t echo_end = DWT->CYCCNT; //uint32_t end_time = DWT->CYCCNT; uint32_t pulse_cycles = (echo_end >= echo_start) ? (echo_end - echo_start) : (UINT32_MAX - echo_start + echo_end); // 精确计算脉冲时间(微秒) uint32_t pulse_us = (uint32_t)((uint64_t)pulse_cycles * 1000000 / SystemCoreClock); // 声速补偿(340m/s = 0.034cm/μs) // 距离 = (时间 × 声速) / 2 (往返距离) return (pulse_us * 34 + 5) / 20; // 返回0.1cm单位(+5用于四舍五入) } // ========== 🎯 新增:超声波测距任务 ========== // 真实测距版本,控制电机运行,与舵机同步,不使用USART1发送 _Noreturn void ultrasonicTask(void const * argument) { int32_t last_valid_dist1 = 5000; int32_t last_valid_dist2 = 5000; const int32_t STOP_DISTANCE = 1100; // 5cm停止 const int32_t MIN_DISTANCE = 20; // 2cm (传感器最小有效距离) for(;;) { int32_t dist1 = measureDistance(GPIOB, GPIO_PIN_2, GPIOD, GPIO_PIN_8); int32_t dist2 = measureDistance(GPIOB, GPIO_PIN_3, GPIOD, GPIO_PIN_9); if(dist1 > MIN_DISTANCE && dist1 < 50000) last_valid_dist1 = dist1; if(dist2 > MIN_DISTANCE && dist2 < 50000) last_valid_dist2 = dist2; if(servoState != SERVO_IDLE) { // 舵机运动时停止两个电机 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0); __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0); } else{ static uint8_t motor1_stop_count = 0; if(last_valid_dist1 < STOP_DISTANCE) { if(++motor1_stop_count > 2) { // 连续3次检测到障碍才停止 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 0); osDelay(2000); } } else { motor1_stop_count = 0; __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 5000); } static uint8_t motor2_stop_count = 0; if(last_valid_dist2 < STOP_DISTANCE) { if(++motor2_stop_count > 2) { __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0); osDelay(2000); } } else { motor2_stop_count = 0; // 距离比例控制 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 5000); } } osDelay(50); // 缩短延迟至50ms } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ // UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ BaseType_t xHigherPriorityTaskWoken = pdFALSE; if(huart->Instance == USART6) { // 更健壮的帧检测 static uint8_t frame_buffer[32]; static uint8_t frame_index = 0; // 存储接收到的字节 frame_buffer[frame_index] = uart2_rx.datarx; frame_index = (frame_index + 1) % sizeof(frame_buffer); // 检测结束符 0xFF 0xFF 0xFF if(frame_index >= 3 && frame_buffer[frame_index-3] == 0xFF && frame_buffer[frame_index-2] == 0xFF && frame_buffer[frame_index-1] == 0xFF) { // 复制有效数据 memcpy(uart2_rx.data, frame_buffer, frame_index-3); uart2_rx.num = frame_index-3; uart2_rx.ok = 1; frame_index = 0; xSemaphoreGiveFromISR(rxpingmuHandle, &xHigherPriorityTaskWoken); } HAL_UART_Receive_IT(&huart6, (uint8_t*)&uart2_rx.datarx, 1); } if(huart ->Instance == USART1){ HAL_GPIO_TogglePin(led1_GPIO_Port,led1_Pin); if(uart3_rx.datarx=='@') uart3_rx.num=0; uart3_rx.data[uart3_rx.num]=uart3_rx.datarx; uart3_rx.num++; if(uart3_rx.num>=28){ uart3_rx.num=0; if(uart3_rx.data[27]==']'){ uart3_rx.ok=1; xSemaphoreGiveFromISR(rxlubancatHandle, &xHigherPriorityTaskWoken); } uart3_rx.num = 0; // 重置缓冲区 } HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart3_rx.datarx, 1); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } /* USER CODE END Application */ 这个程序为什么串口六不发送信号,怎么解决
最新发布
08-01
<think>这个程序中的串口六(USART6)不发送信号,可能的原因有多个方面。我们需要逐一排查。以下是一些可能的原因和相应的解决方法: ### 可能原因分析: 1. **串口初始化问题**:检查USART6的初始化是否正确(在`main.c`或对应的初始化文件中)。包括波特率、数据位、停止位、流控等配置是否匹配。 2. **硬件连接问题**:检查USART6的TX引脚(通常是PG14或PC6,具体看电路图)是否连接正确,以及电平转换电路(如果使用)是否正常。 3. **发送函数被阻塞**:在`PlayAudio`函数中,使用了`osMutexWait`来获取互斥量,如果互斥量一直被其他任务占用,将导致发送阻塞。 4. **互斥量创建问题**:检查互斥量`uart6MutexHandle`是否成功创建。在`MX_FREERTOS_Init`函数中,我们创建了互斥量,但需要确认创建成功。 5. **USART6的发送函数问题**:`HAL_UART_Transmit`函数在中断中使用可能会出现问题,但这里是在任务中调用的。不过,需要确保在调用发送函数时,USART6没有正在进行其他传输(比如中断接收)。 6. **串口助手设置问题**:如果使用串口助手查看,需要确认串口助手设置的波特率等参数与USART6配置一致,且串口线连接正确。 ### 解决步骤: #### 1. 检查USART6初始化 在`main.c`中,检查`MX_USART6_UART_Init`函数是否被调用,并且配置正确(波特率等)。例如: ```c huart6.Instance = USART6; huart6.Init.BaudRate = 115200; huart6.Init.WordLength = UART_WORDLENGTH_8B; huart6.Init.StopBits = UART_STOPBITS_1; huart6.Init.Parity = UART_PARITY_NONE; huart6.Init.Mode = UART_MODE_TX_RX; huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart6.Init.OverSampling = UART_OVERSAMPLING_16; ``` #### 2. 检查互斥量 在`MX_FREERTOS_Init`函数中,我们创建了互斥量: ```c osMutexDef(uart6Mutex); uart6MutexHandle = osMutexCreate(osMutex(uart6Mutex)); ``` 确保互斥量创建成功(非NULL)。可以在创建后添加检查: ```c if (uart6MutexHandle == NULL) { // 创建失败,处理错误 } ``` #### 3. 检查发送函数 在`PlayAudio`函数中,我们使用互斥量保护USART6发送: ```c void PlayAudio(uint8_t audio_num) { char cmd[] = {0x70,0x6C,0x61,0x79,0x20,0x30,0x2C,0x30,0x2C,0x30, 0xFF,0xFF,0xFF}; cmd[7] = '0' + audio_num; osMutexWait(uart6MutexHandle, osWaitForever); HAL_UART_Transmit(&huart6, (uint8_t *)cmd, sizeof(cmd), HAL_MAX_DELAY); osMutexRelease(uart6MutexHandle); } ``` 注意:`sizeof(cmd)`是13,这13个字节会被发送。但是,如果互斥量被其他任务长期占用,这个任务就会一直等待。可以尝试将`osWaitForever`改为一个超时时间,比如`osDelay(100)`,然后检查返回值,看是否因为获取不到互斥量而失败。 #### 4. 检查USART6中断接收 在`HAL_UART_RxCpltCallback`中,我们对USART6的接收中断进行了处理。但是,如果接收中断一直开启,并且频繁接收数据,可能会影响发送。不过,发送函数是阻塞式的,会等待发送完成,所以一般不会冲突。 #### 5. 使用示波器或逻辑分析仪 如果有条件,使用示波器或逻辑分析仪检查USART6的TX引脚是否有波形输出。如果没有,则可能是软件配置问题;如果有,则可能是硬件连接或串口助手问题。 #### 6. 简化测试 为了排除程序其他部分的影响,可以写一个简单的任务,定期通过USART6发送数据,比如: ```c void testUART6Task(void const * argument) { for(;;) { char testMsg[] = "Hello USART6!\r\n"; osMutexWait(uart6MutexHandle, osWaitForever); HAL_UART_Transmit(&huart6, (uint8_t*)testMsg, strlen(testMsg), HAL_MAX_DELAY); osMutexRelease(uart6MutexHandle); osDelay(1000); } } ``` 然后在`MX_FREERTOS_Init`中创建这个任务。如果这个任务能正常发送,说明USART6硬件和初始化没问题,问题出在`PlayAudio`的调用上。 #### 7. 检查`PlayAudio`的调用 在`txTask`任务中,调用`PlayAudio`的地方,注意`play`数组的构造以及发送命令的格式。另外,检查`HAL_UART_Transmit`的返回值,看是否发送成功。 ### 修改建议: 1. 在`PlayAudio`函数中,添加对互斥量获取状态的检查: ```c if (osMutexWait(uart6MutexHandle, 100) != osOK) { // 获取互斥量失败,处理错误 return; } ``` 这样避免任务因互斥量而永久阻塞。 2. 检查`huart6`实例是否在别的地方被修改,比如在其他任务中调用了`HAL_UART_Transmit`或`HAL_UART_Receive`而没有使用互斥量保护。 3. 确认USART6的时钟使能,在`main.c`的`HAL_UART_MspInit`函数中,是否有对USART6的时钟和GPIO进行初始化。 ### 总结: 首先,通过简化测试任务验证USART6能否正常发送。如果能,则问题可能在于`PlayAudio`没有被正确调用或互斥量问题。如果不能,则检查硬件连接和初始化配置。 根据提供的代码,我们注意到在`txTask`任务中,有多个地方调用了`HAL_UART_Transmit`函数来通过USART6发送数据。如果这些发送都没有成功,那么需要按照上述步骤进行排查。 另外,注意在`txTask`任务中,发送数据时没有使用互斥量保护(除了在`PlayAudio`中)。但是,在发送`start`数组、`aa`数组等时,应该使用互斥量保护,因为USART6是共享资源。建议将这些发送也用互斥量保护起来。 例如,在`txTask`中,将需要连续发送的多段数据合并成一次发送(因为多次发送之间可能被中断),或者用互斥量保护整个发送过程: ```c osMutexWait(uart6MutexHandle, osWaitForever); HAL_UART_Transmit(&huart6, (uint8_t *) start,sizeof(start),0xFFFF); HAL_UART_Transmit(&huart6, (uint8_t *)aa, 2,0xFFFF); // ... 其他发送 osMutexRelease(uart6MutexHandle); ``` 但是注意,长时间占用串口可能会影响其他任务。因此,建议将需要发送的数据先组合到一个缓冲区中,然后一次性发送。 由于问题具体原因需要实际调试,以上提供的是常见问题的排查方向。</think>您的程序串口六(USART6)不发送信号,可能由以下原因导致。我将提供详细分析和解决方案: ### 问题分析 1. **互斥量阻塞问题**: - `PlayAudio()`函数使用`osMutexWait(uart6MutexHandle, osWaitForever)`等待互斥- 但代码中**没有释放互斥量**的逻辑(如`osMutexRelease()`),导致任务永久阻塞 2. **任务优先级冲突**: - `txTask`任务优先级为`osPriorityNormal` - 超声波任务`ultrasonicTask`优先级为`osPriorityHigh` - 高优先级任务可能抢占串口发送任务 3. **串口配置问题**: - 缺少USART6的GPIO/TIM初始化验证 - 未确认`huart6`实例是否在CubeMX正确配置 4. **缓冲区溢出风险**: - USART6接收中断使用静态缓冲区`frame_buffer[32]` - 未处理帧过长导致的溢出 ### 解决方案 #### 1. 修复互斥量阻塞(关键) 在`PlayAudio()`函数中添加互斥量释放: ```c void PlayAudio(uint8_t audio_num) { char cmd[] = {0x70,0x6C,0x61,0x79,0x20,0x30,0x2C,0x30,0x2C,0x30, 0xFF,0xFF,0xFF}; cmd[7] = '0' + audio_num; if(osMutexWait(uart6MutexHandle, 100) == osOK) { // 增加超时 HAL_UART_Transmit(&huart6, (uint8_t *)cmd, sizeof(cmd), 100); // 添加超时 osMutexRelease(uart6MutexHandle); // 关键!释放互斥量 } else { // 处理超时错误 Error_Handler(); } } ``` #### 2. 优化任务优先级 调整任务优先级避免抢占: ```c // 在 MX_FREERTOS_Init() 中修改 osThreadDef(txTask, txTask, osPriorityAboveNormal, 2, 128); // 提高优先级 osThreadDef(ultrasonicTask, ultrasonicTask, osPriorityNormal, 0, 256); // 降低优先级 ``` #### 3. 增强USART6接收中断 修复缓冲区溢出风险: ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART6) { static uint8_t frame_buffer[64]; static uint8_t frame_index = 0; // 溢出保护 if(frame_index >= sizeof(frame_buffer)) { frame_index = 0; } frame_buffer[frame_index++] = uart2_rx.datarx; // 检测结束符 0xFF 0xFF 0xFF if(frame_index >= 3 && frame_buffer[frame_index-3] == 0xFF && frame_buffer[frame_index-2] == 0xFF && frame_buffer[frame_index-1] == 0xFF) { // 复制有效数据 uint8_t len = (frame_index-3 < sizeof(uart2_rx.data)) ? frame_index-3 : sizeof(uart2_rx.data); memcpy(uart2_rx.data, frame_buffer, len); uart2_rx.num = len; uart2_rx.ok = 1; frame_index = 0; xSemaphoreGiveFromISR(rxpingmuHandle, &xHigherPriorityTaskWoken); } HAL_UART_Receive_IT(&huart6, (uint8_t*)&uart2_rx.datarx, 1); } // ...其他串口处理... } ``` #### 4. 添加硬件初始化检查 在`main.c`中确认: ```c // 检查GPIO配置 void SystemClock_Config(void) { __HAL_RCC_GPIOG_CLK_ENABLE(); // 确保USART6 TX/RX端口时钟使能 __HAL_RCC_USART6_CLK_ENABLE(); } // 在main()中添加调试引脚 HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_SET); // USART6_TX测试点 ``` #### 5. 添加调试输出 在`txTask`中添加调试信息: ```c _Noreturn void txTask(void const * argument) { // ... for(;;) { // 添加串口状态监测 if(huart6.gState != HAL_UART_STATE_READY) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 视觉指示 } // ... } } ``` ### 验证步骤 1. 使用逻辑分析仪检查USART6_TX(PG14)引脚信号 2. 在`PlayAudio()`函数入口添加LED闪烁指示 3. 检查CubeMX中USART6配置: - 波特率:115200 - 数据位:8-bit - 停止位:1-bit - 硬件流控:Disable 4. 使用示波器测量PG14引脚电压(应为3.3V) ### 注意事项 - 确保在`stm32xxxx_hal_msp.c`中正确配置USART6 - 检查开发板USART6是否被其他外设复用 - 确认终端电阻配置(120Ω端接电阻)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值