/*计算数值积分(函数指针作为函数参数示例*/

该程序演示了如何利用函数指针计算数学函数的平均值。它定义了一个通用的`calc`函数,该函数接受一个双精度浮点型的函数指针,以及两个边界值,然后计算在给定区间内函数值的平均值。示例中分别计算了平方函数`f1`和正弦函数`f2`在不同区间的平均值。
#include<stdio.h>
#include<math.h>
double calc(double(*funp)(double),double a,double b);
double f1(double x),f2(double x);
int main()
{
    double result;
    double(*funp)(double);

    result=calc(f1,0.0,1.0);
    printf("1:result=%.4f\n",result);
    funp=f2;
    result=calc(funp,1.0,2.0);
    printf("2:result=%.4f\n",result);
    return 0;
}
double calc(double(*funp)(double),double a,double b)
{
    double z;
    z=(b-a)/2*((*funp)(a)+(*funp)(b));
    return (z);
  }  
   double f1(double x)
    {
        return(x*x);
    }
    double f2(double x)
    {
        return(sin(x)/x);
    }

/*************************************************************************** * @file power_data.c * @brief * **************************************************************************** * @attention * * Created on: 2025-05-12 * Author: YL Monitor Software group * **************************************************************************** * @description * 功率部件的数据缓存处理模块 * * ****************************************************************************/ /************************ Includes *************************/ /************************ 宏指令 *************************/ #include "power_data.h" /************************ Private types *************************/ /************************ Private constants *************************/ /************************ 功能结构体 *************************/ typedef enum{ CACHE_L1_LOADING = 0xA1,/*正在加载数据*/ CACHE_L1_READY = 0xA2,/*数据就绪*/ CACHE_L1_SENT = 0xA2,/*数据已上传至LEVEL2*/ }ENUM_CACHE_L1_STATUS; typedef enum{ CACHE_L2_SENDING = 0x55,/*数据待上传*/ CACHE_L2_SENT = 0xAA,/*数据已上传*/ }ENUM_CACHE_L2_STATUS; /************************ Private macros *************************/ /************************ Private variables *************************/ /************************ 私有变量 *************************/ #if !SERIAL1_DMARx_ENABLE //禁用DMA1读取 0 /* 一级数据缓存:用于功率部件接收数据的实时缓存 */ uint8_t power1_data_cache_L1[POWER_DEVICE_DATA_SIZE] = {0}; static SemaphoreHandle_t mutex_RW_Power1_L1 = NULL; /* 一级缓存当前数据写入位置 */ static uint16_t power1_L1_wPos = 0; /* 一级数据缓存状态 */ static uint16_t power1_L1_status = CACHE_L1_LOADING; #endif #if !SERIAL2_DMARx_ENABLE //禁用DMA2读取 0 /* 一级数据缓存:用于功率部件接收数据的实时缓存 */ uint8_t power2_data_cache_L1[POWER_DEVICE_DATA_SIZE] = {0}; static SemaphoreHandle_t mutex_RW_Power2_L1 = NULL; /* 一级缓存当前数据写入位置 */ static uint16_t power2_L1_wPos = 0; /* 一级数据缓存状态 */ static uint16_t power2_L1_status = CACHE_L1_LOADING; #endif /* 二级数据缓存:用于系统状态监控 */ static uint8_t power1_data_cache_L2[POWER_DEVICE_DATA_SIZE] = {0}; static uint8_t power2_data_cache_L2[POWER_DEVICE_DATA_SIZE] = {0}; static SemaphoreHandle_t mutex_RW_Power1_L2 = NULL; static SemaphoreHandle_t mutex_RW_Power2_L2 = NULL; /* 二级数据缓存状态 */ static uint8_t power1_L2_status = CACHE_L2_SENDING; static uint8_t power2_L2_status = CACHE_L2_SENDING; /************************ Functions *************************/ /************************ 功能函数 *************************/ /************************************************************ * @funName : MD_SwInitPowerData * @Input : NULL * * @Output : ***************** * @Description : 数据缓存模块软件资源初始化 * * ***************** * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/5/12 * *************************************************************/ void MD_SwInitPowerData(void) { #if !SERIAL1_DMARx_ENABLE if(NULL == mutex_RW_Power1_L1) { mutex_RW_Power1_L1 = xSemaphoreCreateBinary(); /* 数据读写互斥量创建失败 */ if(NULL == mutex_RW_Power1_L1) { } else { /* 释放数据读写互斥量 */ xSemaphoreGive(mutex_RW_Power1_L1); } } #endif #if SERIAL2_DMARx_ENABLE #else if(NULL == mutex_RW_Power2_L1) { mutex_RW_Power2_L1 = xSemaphoreCreateBinary(); /* 数据读写互斥量创建失败 */ if(NULL == mutex_RW_Power2_L1) { } else { /* 释放数据读写互斥量 */ xSemaphoreGive(mutex_RW_Power2_L1); } } #endif if(NULL == mutex_RW_Power1_L2) { mutex_RW_Power1_L2 = xSemaphoreCreateBinary(); /* 数据读写互斥量创建失败 */ if(NULL == mutex_RW_Power1_L2) { } else { /* 释放数据读写互斥量 */ xSemaphoreGive(mutex_RW_Power1_L2); } } if(NULL == mutex_RW_Power2_L2) { mutex_RW_Power2_L2 = xSemaphoreCreateBinary(); /* 数据读写互斥量创建失败 */ if(NULL == mutex_RW_Power2_L2) { } else { /* 释放数据读写互斥量 */ xSemaphoreGive(mutex_RW_Power2_L2); } } } /************************************************************ * @funName : MD_UpdatePowerL2 * @Input : device-功率部件序号 * * @Output : ***************** * @Description : 更新功率部件二级缓存数据 * * ***************** * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/5/12 * *************************************************************/ void MD_UpdatePowerL2(const uint8_t device) { switch(device) { case POWER_DEVICE_1: { #if SERIAL1_DMARx_ENABLE if(BSP_GetRecvSize4Serial1() >= POWER_DEVICE_DATA_SIZE) { uint8_t rbuf[POWER_DEVICE_DATA_SIZE] = {0}; uint16_t rlen = 0; BSP_Recv4Serial1(rbuf, &rlen); if(rlen >= POWER_DEVICE_DATA_SIZE){ portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power1_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)power1_data_cache_L2, (uint8_t*)rbuf, POWER_DEVICE_DATA_SIZE); power1_L2_status = CACHE_L2_SENDING;/* 待发送 */ xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power1_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); } } #else if(CACHE_L1_READY == power1_L1_status) { portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power1_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)power1_data_cache_L2, (uint8_t*)power1_data_cache_L1, POWER_DEVICE_DATA_SIZE); power1_L1_status = CACHE_L1_SENT; power1_L2_status = CACHE_L2_SENDING;/* 待发送 */ xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power1_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); } #endif }break; case POWER_DEVICE_2: { #if SERIAL2_DMARx_ENABLE if(BSP_GetRecvSize4Serial2() >= POWER_DEVICE_DATA_SIZE) { uint8_t rbuf[POWER_DEVICE_DATA_SIZE] = {0}; uint16_t rlen = 0; BSP_Recv4Serial2(rbuf, &rlen); if(rlen >= POWER_DEVICE_DATA_SIZE){ portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power2_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)power2_data_cache_L2, (uint8_t*)rbuf, POWER_DEVICE_DATA_SIZE); power2_L2_status = CACHE_L2_SENDING;/* 待发送 */ xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power2_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); } } #else if(CACHE_L1_READY == power2_L1_status) { portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power2_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)power2_data_cache_L2, (uint8_t*)power2_data_cache_L1, POWER_DEVICE_DATA_SIZE); power1_L1_status = CACHE_L1_SENT; power2_L2_status = CACHE_L2_SENDING;/* 待发送 */ xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power2_L2, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); } #endif }break; } } /************************************************************ * @funName : MD_UpdatePowerL1 * @Input : device-功率部件序号 * bFirst-是否为第一个数据 * wbuf-数据 * wlen-数据长度 * * @Output : ***************** * @Description : 更新功率部件一级缓存数据 * * ***************** * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/5/12 * *************************************************************/ //static uint8_t byte = 0; void MD_UpdatePowerL1(const uint8_t device, const bool bFirst, const uint8_t* wbuf, const uint16_t wlen) { uint16_t len = wlen; if(wlen <= 0) { return; } switch(device) { case POWER_DEVICE_1: { #if SERIAL1_DMARx_ENABLE #else if(bFirst) { power1_L1_status = CACHE_L1_LOADING; power1_L1_wPos = 0; memset((uint8_t*)power1_data_cache_L1, 0, POWER_DEVICE_DATA_SIZE); } if(len > POWER_DEVICE_DATA_SIZE - power1_L1_wPos) { len = POWER_DEVICE_DATA_SIZE - power1_L1_wPos; } portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power1_L1, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)&power1_data_cache_L1[power1_L1_wPos], wbuf, len); power1_L1_wPos += len; xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power1_L1, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); if(POWER_DEVICE_DATA_SIZE <= power1_L1_wPos) { power1_L1_status = CACHE_L1_READY; } #endif }break; case POWER_DEVICE_2: { #if SERIAL2_DMARx_ENABLE #else if(bFirst) { power2_L1_status = CACHE_L1_LOADING; power2_L1_wPos = 0; memset((uint8_t*)power2_data_cache_L1, 0, POWER_DEVICE_DATA_SIZE); } if(len > POWER_DEVICE_DATA_SIZE - power2_L1_wPos) { len = POWER_DEVICE_DATA_SIZE - power2_L1_wPos; } portBASE_TYPE xRecvWoken = pdFALSE; xSemaphoreTakeFromISR(mutex_RW_Power2_L1, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); memcpy((uint8_t*)&power2_data_cache_L1[power2_L1_wPos], wbuf, len); power2_L1_wPos += len; xRecvWoken = pdFALSE; xSemaphoreGiveFromISR(mutex_RW_Power2_L1, &xRecvWoken); portYIELD_FROM_ISR(xRecvWoken); if(POWER_DEVICE_DATA_SIZE <= power2_L1_wPos) { power2_L1_status = CACHE_L1_READY; } #endif }break; } } /********************功率部件一级缓存数据********************/ /************************************************************ * @funName : MD_ReadPowerL2 * @Input : device-功率部件序号 * rbuf-数据输出缓存 * pos-数据读取位置 * rlen-数据读取长度 * * @Output : ***************** * @Description : 获取功率部件二级缓存数据 * * ***************** * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/5/13 * *************************************************************/ bool MD_ReadPowerL2(const uint8_t device, uint8_t *rbuf, const uint16_t pos, const uint16_t rlen) { if(rlen > POWER_DEVICE_DATA_SIZE || pos >= POWER_DEVICE_DATA_SIZE || POWER_DEVICE_DATA_SIZE - pos < rlen) { return false; } switch(device) { case POWER_DEVICE_1: { xSemaphoreTake(mutex_RW_Power1_L2, portMAX_DELAY); memcpy(rbuf, (uint8_t*)&power1_data_cache_L2[pos], rlen); xSemaphoreGive(mutex_RW_Power1_L2); }break; case POWER_DEVICE_2: { xSemaphoreTake(mutex_RW_Power2_L2, portMAX_DELAY); memcpy(rbuf, (uint8_t*)&power2_data_cache_L2[pos], rlen); xSemaphoreGive(mutex_RW_Power2_L2); }break; } return true; } /************************************************************ * @funName : MD_GetPowerL2 * @Input : device-功率部件序号 * rbuf-数据缓存地址 * * @Output : ***************** * @Description : 获取功率部件二级缓存地址 * * ***************** * @Athor : YL Software Group * @Version : V0.0.0 * @Data : 2025/5/13 * *************************************************************/ uint8_t* MD_GetPowerL2(const uint8_t device) { uint8_t* addr = NULL; switch(device) { case POWER_DEVICE_1: { xSemaphoreTake(mutex_RW_Power1_L2, portMAX_DELAY); if(CACHE_L2_SENDING != power1_L2_status) { addr = NULL; } else { power1_L2_status = CACHE_L2_SENT; addr = power1_data_cache_L2; } xSemaphoreGive(mutex_RW_Power1_L2); }break; case POWER_DEVICE_2: { xSemaphoreTake(mutex_RW_Power2_L2, portMAX_DELAY); if(CACHE_L2_SENDING != power2_L2_status) { addr = NULL; } else{ power2_L2_status = CACHE_L2_SENT; addr = power2_data_cache_L2; } xSemaphoreGive(mutex_RW_Power1_L2); }break; } return addr; } /************************ End of file *************************/ /*************************************************************************** * @file fw_data.h * @brief This file contains the macros & function about real data for framework & App. * **************************************************************************** * @attention * * Created on: 2025-05-30 * Author: YL Monitor Software group * **************************************************************************** * @description * * * ****************************************************************************/ #ifndef __FW_DATA_H_ #define __FW_DATA_H_ #ifdef __cplusplus extern "C" { #endif /************************ Includes *************************/ #include "main.h" /************************ Exportd types ********************/ typedef struct{ uint8_t byte_H; uint8_t byte_L; }Power_Bits16; typedef struct{ uint8_t byte0; uint8_t byte1; uint8_t byte2; uint8_t byte3; }Power_Bits32; /* 功率部件系统参数 */ typedef struct{ /* word 0 */ Power_Bits16 Reserved0; //0-预留 /* word 1 */ Power_Bits16 SYSCTRL; //1-系统控制 /* word 2 */ Power_Bits16 Flag; //2-系统状态标志 /* word 3 */ Power_Bits16 ProtectHard_PROHARD; //3-硬件保护标志 /* word 4 */ Power_Bits16 ProtectSoft_PROSOFT; //4-软件保护标志 /* word 5 */ Power_Bits16 ProtectDrive_PRODRIVE; //5-驱动保护标志 /* word 6 */ Power_Bits16 ProtectComm_PROCOMM; //6-通信保护标志 /* word 7 */ Power_Bits16 INVCTRL; //7-逆变器控制 /* word 8 */ Power_Bits16 Reserved8; //预留 /* word 9 */ Power_Bits16 Reserved9; //预留 /* word 10 */ Power_Bits16 Reserved10; //预留 /* word 11 */ Power_Bits32 GPADAT_H; //GPIO0~31状态 /* word 12 */ Power_Bits32 GPADAT_L; //GPIO0~31状态 /* word 13*/ Power_Bits32 GPBDAT_H; //GPIO32~63状态 /* word 14 */ Power_Bits32 GPBDAT_L; //GPIO32~63状态 /* word 15 */ Power_Bits32 GPCDAT_H; //GPIO64~87状态 /* word 16 */ Power_Bits32 GPCDAT_L; //GPIO64~87状态 /* word 17 */ Power_Bits16 Reserved17; //预留 /* word 18 */ Power_Bits16 Reserved18; //预留 /* word 19 */ Power_Bits16 Reserved19; //预留 /* word 20 */ Power_Bits16 OSC_CLK_FRQ; //外部晶振频率 /* word 21 */ Power_Bits16 SYS_CLK_FRQ; //系统时钟频率 /* word 22 */ Power_Bits16 SYS_TICK; //定时器时钟基准 /* word 23 */ Power_Bits16 SET_F_PWM; //开关频率 /* word 24 */ Power_Bits16 Reserved24; //预留 /* word 25 */ Power_Bits16 SysMode; //工作模式 /* word 26 */ Power_Bits16 SysState; //工作状态 /* word 27 */ Power_Bits16 SysStartMode; //启动方式 /* word 28*/ Power_Bits16 SysStartStopControl; //启停控制指令来源 /* word 29*/ Power_Bits16 SysCommandSource; //系统频率指令来源 /* word 30*/ Power_Bits16 ModID; //模块编号 /* word 31*/ Power_Bits16 SETUP_UOUT; //电压设定值 /* word 32*/ Power_Bits16 SETUP_IOUT; //电流设定值 /* word 33*/ Power_Bits16 SETUP_FREQ; //频率设定值 /* word 34*/ Power_Bits16 SOFTSTART_TIME; //软件起动时间 /* word 35*/ Power_Bits16 STEP_UOUT; //电压步长 /* word 36*/ Power_Bits16 STEP_IOUT; //电流步长 /* word 37*/ Power_Bits16 STEP_FREQ; //频率步长 /* word 38 */ Power_Bits16 STEP_ANGLE; //相角步长 /* word 39 */ Power_Bits16 POINTCYCLE; //周波点数 /* word 40 */ Power_Bits16 REF_UOUT; //电压给定值 /* word 41 */ Power_Bits16 REF_IOUT; //电流给定值 /* word 42 */ Power_Bits16 REF_FREQ; //频率给定值 /* word 43 */ Power_Bits16 REF_ANGLE; //实时相角 /* word 44 */ Power_Bits16 KPWMA; //A相调制系数 /* word 45 */ Power_Bits16 KPWMB; //B相调制系数 /* word 46 */ Power_Bits16 KPWMC; //C相调制系数 /* word 47 */ Power_Bits16 Effective_Uin; //输入电压有效值 /* word 48 */ Power_Bits16 Effective_Iin; //输入电流有效值 /* word 49 */ Power_Bits16 Effective_Udc; //直流母线电压有效值 /* word 50 */ Power_Bits16 Effective_Uout1; //A相输出电压有效值 /* word 51 */ Power_Bits16 Effective_Uout2; //B相输出电压有效值 /* word 52 */ Power_Bits16 Effective_Uout3; //C相输出电压有效值 /* word 53 */ Power_Bits16 Effective_Iout1; //A相输出电流有效值 /* word 54 */ Power_Bits16 Effective_Iout2; //B相输出电流有效值 /* word 55 */ Power_Bits16 Effective_Iout3; //C相输出电流有效值 /* word 56 */ Power_Bits16 Effective_IL1; //A相电感电流有效值 /* word 57 */ Power_Bits16 Effective_IL2; //B相电感电流有效值 /* word 58 */ Power_Bits16 Effective_IL3; //C相电感电流有效值 /* word 59 */ Power_Bits16 Effective_UinC; //备用电源电压有效值 /* word 60 */ Power_Bits16 Effective_UoutSet; //输出电压设定值(模拟) /* word 61 */ Power_Bits16 Effective_IoutSet; //输出电流设定值(模拟) /* word 62 */ Power_Bits16 Reserved62; //预留 /* word 63 */ Power_Bits16 Effective_FreqSet; //输出电压频率设定值(模拟) /* word 64 */ Power_Bits16 PIDU1_hReference; //PIDU1给定值 /* word 65 */ Power_Bits16 PIDI1_hPresentFeedback; //PIDI1反馈值 /* word 66 */ Power_Bits16 PIDI1_hReference; //PIDI1输出值 /* word 67 */ Power_Bits16 PIDU1_hKp_Gain; //PIDU1参数kp /* word 68*/ Power_Bits16 PIDU1_hKi_Gain; //PIDU1参数ki /* word 69*/ Power_Bits16 PIDU1_hKd_Gain; //PIDU1参数kd /* word 70*/ Power_Bits32 PIDU1_wLower_Limit_Integral; //PIDU1积分下限值 /* word 71*/ Power_Bits32 PIDU1_wUpper_Limit_Integral; //PIDU1积分上限值 /* word 72*/ Power_Bits16 PIDU1_hLower_Limit_Output; //PIDU1输出下限值 /* word 73*/ Power_Bits16 PIDU1_hUpper_Limit_Output; //PIDU1输出上限值 /* word 74*/ Power_Bits16 PIDU2_hReference; //PIDU2给定值 /* word 75*/ Power_Bits16 PIDU2_hPresentFeedback; //PIDU2反馈值 /* word 76*/ Power_Bits16 PIDI2_hReference; //PIDI2输出值 /* word 77*/ Power_Bits16 PIDU2_hKp_Gain; //PIDU2参数kp /* word 78*/ Power_Bits16 PIDU2_hKi_Gain; //PIDU2参数ki /* word 79*/ Power_Bits16 PIDU2_hKd_Gain; //PIDU2参数kd /* word 80*/ Power_Bits32 PIDU2_wLower_Limit_Integral; //PIDU2积分下限值 /* word 81*/ Power_Bits32 PIDU2_wUpper_Limit_Integral; //PIDU2积分上限值 /* word 82*/ Power_Bits16 PIDU2_hLower_Limit_Output; //PIDU2输出下限值 /* word 83*/ Power_Bits16 PIDU2_hUpper_Limit_Output; //PIDU2输出上限值 /* word 84 */ Power_Bits16 PIDI1hReference; //PIDI1给定值 /* word 85 */ Power_Bits16 PIDI1hPresentFeedback; //PIDI1反馈值 /* word 86 */ Power_Bits16 iParkUref_Ds; //PIDI1输出值 /* word 87 */ Power_Bits16 PIDI1_hKp_Gain; //PIDI1参数kp /* word 88*/ Power_Bits16 PIDI1_hKi_Gain; //PIDI1参数ki /* word 89*/ Power_Bits16 PIDI1_hKd_Gain; //PIDI1参数kd /* word 90*/ Power_Bits32 PIDI1_wLower_Limit_Integral; //PIDI1积分下限值 /* word 91*/ Power_Bits32 PIDI1_wUpper_Limit_Integral; //PIDI1积分上限值 /* word 92*/ Power_Bits16 PIDI1_hLower_Limit_Output; //PIDI1输出下限值 /* word 93*/ Power_Bits16 PIDI1_hUpper_Limit_Output; //PIDI1输出上限值 /* word 94 */ Power_Bits16 PIDI2hReference; //PIDI2给定值 /* word 95 */ Power_Bits16 PIDI2_hPresentFeedback; //PIDI2反馈值 /* word 96 */ Power_Bits16 iParkUref_Qs; //输出值 /* word 97 */ Power_Bits16 PIDI2_hKp_Gain; //PIDI2参数kp /* word 98*/ Power_Bits16 PIDI2_hKi_Gain; //PIDI2参数ki /* word 99*/ Power_Bits16 PIDI2_hKd_Gain; //PIDI2参数kd /* word 100*/ Power_Bits32 PIDI2_wLower_Limit_Integral; //PIDI2积分下限值 /* word 101*/ Power_Bits32 PIDI2_wUpper_Limit_Integral; //PIDI2积分上限值 /* word 102*/ Power_Bits16 PIDI2_hLower_Limit_Output; //PIDI2输出下限值 /* word 103*/ Power_Bits16 PIDI2_hUpper_Limit_Output; //PIDI2输出上限值 /* word 104 */ Power_Bits16 PIDPARA_hReference; //PIDPARA给定值 /* word 105 */ Power_Bits16 PIDPARA_hPresentFeedback; //PIDPARA反馈值 /* word 106 */ Power_Bits16 Reserved106; //PIDPARA输出值 /* word 107 */ Power_Bits16 PIDPARA_hKp_Gain; //PIDPARA参数kp /* word 108*/ Power_Bits16 PIDPARA_hKi_Gain; //PIDPARA参数ki /* word 109*/ Power_Bits16 PIDPARA_hKd_Gain; //PIDPARA参数kd /* word 110*/ Power_Bits32 PIDPARA_wLower_Limit_Integral;//PIDPARA积分下限值 /* word 111*/ Power_Bits32 PIDPARA_wUpper_Limit_Integral;//PIDPARA积分上限值 /* word 112*/ Power_Bits16 PIDPARA_hLower_Limit_Output; //PIDPARA输出下限值 /* word 113*/ Power_Bits16 PIDPARA_hUpper_Limit_Output; //PIDPARA输出上限值 /* word 114 */ Power_Bits16 PIDPLL_hReference; //PIDPLL给定值 /* word 115 */ Power_Bits16 PIDPLL_hPresentFeedback; //PIDPLL反馈值 /* word 116 */ Power_Bits16 Reserved116; //PIDPLL输出值 /* word 117 */ Power_Bits16 PIDPLL_hKp_Gain; //PIDPLL参数kp /* word 118*/ Power_Bits16 PIDPLL_hKi_Gain; //PIDPLL参数ki /* word 119*/ Power_Bits16 PIDPLL_hKd_Gain; //PIDPLL参数kd /* word 120*/ Power_Bits32 PIDPLL_wLower_Limit_Integral; //PIDPLL积分下限值 /* word 121*/ Power_Bits32 PIDPLL_wUpper_Limit_Integral; //PIDPLL积分上限值 /* word 122*/ Power_Bits16 PIDPLL_hLower_Limit_Output; //PIDPLL输出下限值 /* word 123*/ Power_Bits16 PIDPLL_hUpper_Limit_Output; //PIDPLL输出上限值 /* word 124 */ Power_Bits16 Reserved124; //输出变压器变比 /* word 125 */ Power_Bits16 Reserved125; //变压器等效电抗 /* word 126 */ Power_Bits16 Reserved126; //变压器等效电阻 /* word 127 */ Power_Bits16 Reserved127; //预留 /* word 128 */ Power_Bits16 FdOverUin_ValLimitHi; //输入过压保护值 /* word 129 */ Power_Bits16 FdUnderUin_ValLimitHi; //输入欠压保护值 /* word 130 */ Power_Bits16 FdOverIin_ValLimitHi; //输入过流保护值 /* word 131 */ Power_Bits16 FdOverUo1_ValLimitHi; //输出过压保护值 /* word 132 */ Power_Bits16 FdOverIo1_ValLimitHi; //输出过流保护值 /* word 133 */ Power_Bits16 FdOverIL1_ValLimitHi; //电感过流保护值 /* word 134 */ Power_Bits16 Reserved134; //短路保护电压动作值 /* word 135 */ Power_Bits16 Reserved135; //短路保护电流动作值 /* word 136 */ Power_Bits16 Reserved136; //短路保护电压返回值 /* word 137 */ Power_Bits16 Reserved137; //短路保护电流返回值 /* word 138 */ Power_Bits16 Reserved138; //短路运行时间 /* word 139 */ Power_Bits16 Reserved139; //110%过载保护限值 /* word 140 */ Power_Bits16 Reserved140; //110%过载保护时间 /* word 141 */ Power_Bits16 Reserved141; //110%过载保护倒计时 /* word 142 */ Power_Bits16 Reserved142; //120%过载保护限值 /* word 143 */ Power_Bits16 Reserved143; //120%过载保护时间 /* word 144 */ Power_Bits16 Reserved144; //120%过载保护倒计时 /* word 145 预留*/ Power_Bits16 Reserved145; /* word 146 AD结果寄存器数据(CH0)*/ Power_Bits16 AdcRegs_ADCRESULT0; /* word 147 */ Power_Bits16 AdcRegs_ADCRESULT1; //AD结果寄存器数据(CH1) /* word 148 */ Power_Bits16 AdcRegs_ADCRESULT2; //AD结果寄存器数据(CH2) /* word 149 */ Power_Bits16 AdcRegs_ADCRESULT3; //AD结果寄存器数据(CH3) /* word 150 */ Power_Bits16 AdcRegs_ADCRESULT4; //AD结果寄存器数据(CH4) /* word 151 */ Power_Bits16 AdcRegs_ADCRESULT5; //AD结果寄存器数据(CH5) /* word 152 */ Power_Bits16 AdcRegs_ADCRESULT6; //AD结果寄存器数据(CH6) /* word 153 */ Power_Bits16 AdcRegs_ADCRESULT7; //AD结果寄存器数据(CH7) /* word 154 */ Power_Bits16 AdcRegs_ADCRESULT8; //AD结果寄存器数据(CH8) /* word 155 */ Power_Bits16 AdcRegs_ADCRESULT9; //AD结果寄存器数据(CH9) /* word 156 */ Power_Bits16 AdcRegs_ADCRESULT10; //AD结果寄存器数据(CH10) /* word 157 */ Power_Bits16 AdcRegs_ADCRESULT11; //AD结果寄存器数据(CH11) /* word 158 */ Power_Bits16 AdcRegs_ADCRESULT12; //AD结果寄存器数据(CH12) /* word 159 */ Power_Bits16 AdcRegs_ADCRESULT13; //AD结果寄存器数据(CH13) /* word 160 */ Power_Bits16 AdcRegs_ADCRESULT14; //AD结果寄存器数据(CH14) /* word 161 */ Power_Bits16 AdcRegs_ADCRESULT15; //AD结果寄存器数据(CH15) /* word 162 预留*/ Power_Bits16 Reserved162; /* word 163 预留*/ Power_Bits16 Reserved163; /* word 164 预留*/ Power_Bits16 Reserved164; /* word 165 预留*/ Power_Bits16 Reserved165; /* word 166 预留*/ Power_Bits16 Reserved166; /* word 167 预留*/ Power_Bits16 Reserved167; /* word 168 预留*/ Power_Bits16 Reserved168; /* word 169预留 */ Power_Bits16 Reserved169; /* word 170 预留*/ Power_Bits16 Reserved170; /* word 171 预留*/ Power_Bits16 Reserved171; /* word 172 预留*/ Power_Bits16 Reserved172; /* word 173 预留*/ Power_Bits16 Reserved173; /* word 174 预留*/ Power_Bits16 Reserved174; /* word 175 预留*/ Power_Bits16 Reserved175; /* word 176 预留*/ Power_Bits16 Reserved176; /* word 177 预留*/ Power_Bits16 Reserved177; /* word 178 预留*/ Power_Bits16 Reserved178; /* word 179 预留*/ Power_Bits16 Reserved179; /* word 180 输入电压传感器采样范围*/ Power_Bits16 PEAK_UIN_SENSOR; /* word 181 输入电流传感器采样范围*/ Power_Bits16 PEAK_IIN_SENSOR; /* word 182 输出电压传感器采样范围*/ Power_Bits16 PEAK_UO_SENSOR; /* word 183 输出电流传感器采样范围*/ Power_Bits16 PEAK_IO_SENSOR; /* word 184 电感电流传感器采样范围*/ Power_Bits16 PEAK_IL_SENSOR; /* word 185 预留*/ Power_Bits16 Reserved185; /* word 186 预留*/ Power_Bits16 Reserved186; /* word 187 预留*/ Power_Bits16 Reserved187; /* word 188 预留*/ Power_Bits16 Reserved188; /* word 189 预留*/ Power_Bits16 Reserved189; /* word 190 通道选择*/ Power_Bits16 ChannelSelect; /* word 191 预留*/ Power_Bits16 Reserved191; /* word 192 预留*/ Power_Bits16 Reserved192; /* word 193 地址偏移量*/ Power_Bits16 AddressOffset; /* word 194 预留*/ Power_Bits16 Reserved194; /* word 195 预留*/ Power_Bits16 Reserved195; /* word 196 预留*/ Power_Bits16 Reserved196; /* word 197 预留*/ Power_Bits16 Reserved197; /* word 198 预留*/ Power_Bits16 Reserved198; /* word 199 网络通讯协议控制*/ Power_Bits16 Modbus_Control_ModbusCtrl; }Power_System_Type; STM32F105 power_data.c程序的二级缓存的400字节的数据准确的映射给fw_data.h程序中的Power_System_Type结构体中,然后去调用结构体的某一个数据,需要频繁访问结构体中的某个字段,可以将其缓存到局部变量中,减少多次访问互斥锁的开销,用标准库写出详细代码和注释,优化的建议也写入代码中,别单独提出来
06-06
/* * motor.c * * Created on: Jul 19, 2025 * Author: HONOR */ #include"motor.h" static pid_typedef pid_motor_l;//左电机pid初始化 static pid_typedef pid_motor_r;//右电机pid初始化 //= //初始化左右电机调速系统 // void motor_init(void) { pid_init(&pid_motor_l,0.5,7,0,+100.0f,-100.0f);//左电机初始化 //pid_limitconfig(&pid_motor_l,+8.4f,-8.4f); pid_init(&pid_motor_r,0.5,7,0,+100.0f,-100.0f);//右电机初始化 //pid_limitconfig(&pid_motor_r,+8.4f,-8.4f); } // //电机调速系统进程函数 // void motor_proc(void) { periodic(1)//每间隔1ms执行一次 //#1.获取左右电机的角速度,单位:弧度每秒 float omega_l=encoder_getspeed_l(); float omega_r=encoder_getspeed_r(); //#2.计算pid的输出 float ua_l= pid_compute(&pid_motor_l, omega_l); float ua_r= pid_compute(&pid_motor_r, omega_r); //#3.将电压设置到电机两端,因为模型是输入电压卷积电机系统得到输出转速 //电压占空比 // //float vbat=bat_get(); float duty_l=ua_l ; ///vbat*100.0f; float duty_r=ua_r ; ///vbat*100.0f; pwm_set_l(duty_l); pwm_set_r(duty_r); } // //设置左电机转速的值 //参数:omega为目标值 //单位:弧度每秒 void motor_setomega_l(float omega) { pid_changesp(&pid_motor_l, omega); } // //设置右电机转速的值 //参数:omega为目标值 //单位:弧度每秒 void motor_setomega_r(float omega) { pid_changesp(&pid_motor_r, omega); } // //开关电机 //参数:on,为0时关闭 void motor_cmd(uint8_t on) { pwm_cmd(on); //控制pwm信号 pid_reset(&pid_motor_l);//复位左电机 pid_reset(&pid_motor_r);//复位右电机 } /* * pid.c * * Created on: Jul 19, 2025 * Author:NHY * #1.声明一个pid_typedef的变量 * #2.初始化变量pid_init * #3.计算 * #4.注意每次启动时记得复位。pid_set */ #include"pid.h" // //对kp,ki,kd,输出上下限进行初始化 // void pid_init(pid_typedef*pid,float kp,float ki,float kd,float upper,float low) { pid->kp=kp; pid->ki=ki; pid->kd=kd; pid->t_k_1=0; pid->err_k_1=0.0f; pid->err_int_k_1=0.0f; pid->up_limit=upper; pid->low_limit=low; // //后续可自行更改 // pid->up_limit=+3.4e+38f; //该值为float类型最大值,及假设初始时pid输出上线无穷大 // pid->low_limit=-3.4e+38f;//该值为float类型最小值,及假设初始时pid输出下线无穷小 } // //设置电机pid输出上下限 //参数:upper输出上限,low输出下限 //void pid_limitconfig(pid_typedef*pid,float upper,float low) //{ // pid->up_limit=upper; // pid->low_limit=low; //} // //对设定值进行更改 // void pid_changesp(pid_typedef*pid,float sp) { pid->sp=sp; } // //执行一次pid运算 //参数:fb传感器反馈值,如电机的实际转速 //返回值:co,pid控制器运算的结果 float pid_compute(pid_typedef*pid,float fb) { float err=pid->sp-fb;//计算误差 uint64_t t_k=Getus(); float deltat=(t_k-pid->t_k_1)*1.0e-6;//微秒转秒,获取时间间隔 //首次运行判断 //首次运行时,因为不存在历史数据,计算的值可能不对,故首次是积分项,微分项应该为零 //首次运行时t_k_1=0; float err_dev=0.0f; float err_int=0.0f; if(pid->t_k_1!=0) { err_dev=(err-pid->err_k_1)/deltat;//微分值 err_int=(pid->err_int_k_1+err)*deltat*0.5f+pid->err_int_k_1;//积分值 } float cop=pid->kp*err;//比例项计算值 float coi=pid->ki*err_int;//积分项计算值 float cod=pid->kd*err_dev;//微分项计算值 float co=coi+cop+cod; //pid计算总结果 //更新数据 pid->t_k_1=t_k; pid->err_k_1=err; pid->err_int_k_1=err_int; //输出限幅 if(co>pid->up_limit) co=pid->up_limit; //输出大于上限时,输出上限 if(co<pid->low_limit) co=pid->low_limit;//输出小于下限时,输出下限 //积分限幅 if(pid->err_int_k_1>pid->up_limit) pid->err_int_k_1=pid->up_limit;//如果本次的积分值超过上限,则下一次的积分值限制在了上限 if(pid->err_int_k_1<pid->low_limit) pid->err_int_k_1=pid->low_limit;//如果本次的积分值低于下限,则下一次的积分值限制在了下限 return co; } // //对pid控制器进行清零,防止前几次的数据干扰 //参数:pid指针 void pid_reset(pid_typedef*pid) { pid->t_k_1=0.0f; //t[k-1],上一次的t值 pid->err_k_1=0.0f; //上一次err[k-1]的值 pid->err_int_k_1=0.0f; //上一次的积分值,err_int[k-1] } /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * <h2><center>© Copyright (c) 2025 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include"button.h" #include"pwm.h" #include"encoder.h" #include"my_usart.h" #include"motor.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_USART2_UART_Init(); /* USER CODE BEGIN 2 */ pwm_init(); motor_init(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { button_proc(); usart_proc(); motor_proc(); float sp=(HAL_GetTick()/1000)%10*2.0f; motor_setomega_l(sp); motor_setomega_l(sp); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 为什么小车没有按设定值运行
07-28
unsigned_word LBFC_SigmaDigitalFiltering (unsigned_word* pFirst, unsigned_word* pLast, const signed_word* pCoeff) { volatile signed_double_long temp, temp2, temp3, temp4, temp5, temp6; signed_double_long vT, vT_1, vT_2; signed_double_long xT, xT_1, xT_2; signed_double_long yT, yT_1, yT_2; signed_double_long SUMi; volatile signed_double_long SUM; volatile signed_word* pFirst_copy; volatile signed_double_long BufferSum[2]; /*signed_word a31,a21,b11,b31,b21,a32,a22,b12,b32,b22,a33,a23,b13,b33,b23;*/ signed_word a31,a21,b11,b31,b21,a32,a22,b32,b22,a33,a23,b33,b23; volatile signed_longword FilterIn; if( (pFirst != NULL) && (pLast != NULL) &&(pCoeff != NULL) ) { pFirst_copy = (signed_word*)pFirst; a31 = pCoeff[1]; a21 = pCoeff[2]; b11 = pCoeff[3]; b31 = pCoeff[4]; b21 = pCoeff[5]; a32 = pCoeff[6]; a22 = pCoeff[7]; /*b12 = pCoeff[8];*/ b32 = pCoeff[9]; b22 = pCoeff[10]; a33 = pCoeff[11]; a23 = pCoeff[12]; /*b13 = pCoeff[13];*/ b33 = pCoeff[14]; b23 = pCoeff[15]; SUM = 0U; vT = 0; vT_1 = 0; vT_2 = 0; xT = 0; xT_1 = 0; xT_2 = 0; yT = 0; yT_1 = 0; yT_2 = 0; while ( pFirst_copy <= (signed_word *)pLast ) { /* Subtract offset */ /* FilterIn = *pFirst_copy - 2871;*/ FilterIn = *pFirst_copy; /* 5*2^-11.5 -> 5*2^-16 */ /* FilterIn value should be normalised depending on the gain and sampling period 5V to be equally spread over 16bit.*/ /*FilterIn = (FilterIn * 22627) / 1000;*/ /*FilterIn = (FilterIn * 12730) / 10000;*/ /*FilterIn = (FilterIn * 25459) / 10000;*/ /*13us 194.24*/ FilterIn = (FilterIn * 21220) / 10000; /*EQ*/ /* RES 5*2-16 */ SUMi = 0U; /* 5*2 ^-16 */ temp = ((b21 * vT_1) + (b31 * vT_2))/16384; /* 5*2 ^-16 */ SUMi += temp; /* 5*2^-16 */ vT = ((((a21 * vT_1) + (a31 * vT_2))/16384) + FilterIn); /* 5 2 ^-16 */ temp2 = (vT * b11)/16384; /* 5*2 ^-16 */ SUMi += temp2; /* 5*2 ^-16 */ temp3 = ((a22 * xT_1) + (a32 * xT_2))/16384; /* 5*2 ^-16 */ SUMi += temp3; /* 5*2 ^16 */ xT = SUMi; /* 5 2 ^-16 */ temp4 = ((b22*xT_1) + (b32*xT_2))/16384; /* 5*2 ^-16 */ SUMi += temp4; /* 5*2 ^-16 */ temp5 = ((a23*yT_1) + (a33*yT_2))/16384; /* 5*2 ^-16 */ SUMi += temp5; /* 5*2 ^16 */ yT = SUMi; /* 5 2 ^-16 */ temp6 = ((b23*yT_1) + (b33*yT_2))/16384; /* 5*2 ^-16 */ SUMi += temp6; if (SUMi < 0) { SUMi = -SUMi; } /* Buffer Output 5*2 ^-16 */ /* Sum results 5*2^-16 */ SUM += SUMi; /* Prepare for next cycle 5*2-16 */ vT_2 = vT_1; vT_1 = vT; xT_2 = xT_1; xT_1 = xT; yT_2 = yT_1; yT_1 = yT; /* Increment pointer */ pFirst_copy = &pFirst_copy[1]; } /* while */ BufferSum[0] = SUM; /* Multiply SUM for GAIN: SUM 5*2-16 and pCoeff[16] 2^0 */ /* 5*2^-46 */ /* SUM * 1/50000 (res 2^-30) -> 21475 ---> SUM RES 5*2^-46 */ SUM = SUM * pCoeff[16]*21475; /* 5*2^-46 -> 5*2^-18 */ SUM = SUM/268435456; BufferSum[1] = SUM; /* 5*2^-18 -> 5*2^-13 */ SUM = SUM/32; /* 5*2^-16 -> 5*2^-13 */ /*SUM = SUM/8;*/ /* 5*2^-13 -> 2^-13 */ SUM = SUM*5; /* 5V saturated */ if (SUM >= 40960) { SUM = 40960; } /* 2^-13 */ return ((unsigned_word)SUM); } else { return(0); } } /* LBF_MySigmaDigitalFilteringFixed */ 上述代码中21220和21475是什么
最新发布
11-21
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超翔之逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值