乐趣区

关于人工智能:N32G457-基于RTThread和N32G457的简易便捷式可调电压源

本文是 RT-Thread 用户 @chengjili 原创公布,是用于加入 RT-Thread 与国民技术联手推出 N32G457 RT-Thread 设计大赛,原文:https://club.rt-thread.org/as…

本设计是用来加入《翻新“芯”引擎 | 国民技术 N32G457 RT-Thread 设计大赛》的作品。初衷是因为做军品要求国产化,所以最近也始终在测试国内不同厂家的 32 芯片的性能以及开发便捷性和最重要的稳定性等问题。之前也始终在用 RTT 做开发。浏览官网的时候发现有这个较量,就顺便加入一下。至于做什么,必定不能做我工作上的货色,都是军品窃密的。只能想一些简略的。正好想到平时测试,特地是外出测试,常常因为电源问题而苦恼。于是想做一款繁难的可调电压源,这样只须要带一个充电宝,就能够输入 1~35V 的电压,峰值电流根本能达到 5A 以上。也就能够驱动大多数的设施了。当然,功耗大的设施须要大充电宝能力工作工夫长一些,但至多解决了供电电压不匹配的问题,能够应急应用了。
因为此较量要求用官网给的外围板做开发,所以打算先简略实现一下主体性能。前面再具体做板实现整体性能。这次的测试硬件如下图,N32 的最小零碎板加一块我这边做其它测试的板子。这块板子只用了 LTC3780 局部的电路。

具体电路如下图:

其中 TEC- 对地就是最终的电源输入接口,TEC_DAC 为输入电压调整端口,接 N32 板的 PA4 引脚。TEC_i 为输入电流检测端口,接 N32 板的 PC1 引脚。电源输入对地飞了个 110K 和 10K 的分压电阻,分压短点接 N32 板的 PC0 引脚,用于测量输入电压值(临时没有对测量电压进行标定)。
测试版没有做显示界面(不太想飞线接屏了🤣)。取而代之的是 shell 界面。设置电压和电流值也用的 shell 命令。前面做电路的时候再改成显示屏和按键以及编码器旋钮的交互。测试界面如下:

具体实现了 shell 命令批改电压值,电流值。输入电压和电流反馈的 ADC 采集。实时显示以后输出功率。PID 管制输入电压值。电流超过电流设置值时触发限流爱护,升高电压。按特定波形输入电压的性能,临时只做了正弦信号的。这个性能后续有需要再持续扩大其它信号。
程序方面,自认为 RTT 自带的一些驱动,效率不是很高,只适宜特定场合的简略应用,或者算是提供了一个 demo。实现具体性能的话,倡议本人重写底层驱动。比方这次用的 shell 底层串口驱动框架和 ADC 驱动框架。我日常应用,大多数都是用其它零碎板通过 shell 命令配置以后板子的各运行参数,这样比起人手输出命令,传输数据量就要高很多。底层解决效率低的话就会呈现丢数据,丢指令和串口配置参数的时候 CPU 占用率过高的问题,所以要适应这种场合我都是要重写底层驱动,退出收发 FIFO。读写都只对软件的 FIFO 接口,不必期待硬件的真正的发送和接管实现,进步了代码执行效率。但这次的测试性能,都是人手输出 shell 命令,所以这部分驱动我没有再改变,就用的原有的,针对于这种测试需要倒是足够了。但加入较量,总要在代码上有点奉献,所以这次次要改变的是 ADC 的底层驱动,退出了集体在工作中对 ADC 应用的一些不成熟的了解,心愿能帮忙到有须要的敌人。上面做具体阐明:
如下是官网驱动,enable ADC 通道和读 ADC 数据的代码:

static rt_err_t n32_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled)
{
ADC_Module *n32_adc_handler;
ADC_InitType ADC_InitStructure;
RT_ASSERT(device != RT_NULL);
n32_adc_handler = device->parent.user_data;

n32_msp_adc_init(n32_adc_handler);

ADC_InitStruct(&ADC_InitStructure);
ADC_InitStructure.WorkMode              = ADC_WORKMODE_INDEPENDENT;
ADC_InitStructure.MultiChEn             = DISABLE;
ADC_InitStructure.ContinueConvEn        = DISABLE;
ADC_InitStructure.ExtTrigSelect         = ADC_EXT_TRIGCONV_NONE;
ADC_InitStructure.DatAlign              = ADC_DAT_ALIGN_R;
ADC_InitStructure.ChsNumber             = 1;
ADC_Init(n32_adc_handler, &ADC_InitStructure);

/* ADCx regular channels configuration */
ADC_ConfigRegularChannel(n32_adc_handler, n32_adc_get_channel(channel), 1, ADC_SAMP_TIME_28CYCLES5);

if (((n32_adc_handler == ADC2) || (n32_adc_handler == ADC2))
    && ((n32_adc_get_channel(channel) == ADC_CH_16) || (n32_adc_get_channel(channel) == ADC_CH_18)))
{ADC_EnableTempSensorVrefint(ENABLE);
}

/* Enable ADCx */
ADC_Enable(n32_adc_handler, ENABLE);

/* Start ADCx calibration */
ADC_StartCalibration(n32_adc_handler);
/* Check the end of ADCx calibration */
while(ADC_GetCalibrationStatus(n32_adc_handler));

if (enabled)
{
    /* Enable ADC1 */
    ADC_Enable(n32_adc_handler, ENABLE);
}
else
{
    /* Enable ADCx */
    ADC_Enable(n32_adc_handler, DISABLE);
}

return RT_EOK;
}
static rt_err_t n32_get_adc_value(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value)
{
ADC_Module *n32_adc_handler;

RT_ASSERT(device != RT_NULL);
RT_ASSERT(value != RT_NULL);

n32_adc_handler = device->parent.user_data;

/* Start ADCx Software Conversion */
ADC_EnableSoftwareStartConv(n32_adc_handler, ENABLE);

/* Wait for the ADC to convert */
while(ADC_GetFlagStatus(n32_adc_handler, ADC_FLAG_ENDC) == RESET);

/* get ADC value */
*value = ADC_GetDat(n32_adc_handler);

return RT_EOK;
}

首先 enable 外面初始化 ADC 就曾经写死了关掉了多通道采集,关掉了间断转换。规定组中也只能退出一个通道。所以,同一时刻只能 enable 一个通道,转换结束后再 enable 下一个通道。每次读取都要使能软件触发转换,而后期待转换实现,而后再读取转换后果返回。这现然不是高效的做法,while 期待的时候 CPU 什么都做不了,也不能调度去执行其它线程的工作。只实用于很偶尔的读取一下 ADC 数据。而大多数读取 ADC 数据根本都是要读取大量数据,做一些平滑滤波或中值滤波,最终失去一些稳固的数据。现然这个框架很难实现此性能,就算勉强用一个线程频繁的去读取 ADC 数据来实现此性能,那预计弱小的 32 芯片也根本干不了别的了。144M 的主频,说高不高,但说低也不低,又有 FPU 能够并行执行一些浮点运算指令,利用好了还是能够实现很多事件的。感觉官网 ADC 驱动框架也根本只限于简略的 demo 应用。于是退出了如下本人的 ADC 驱动程序:

#define ADC1_BUF1_UPDATE_EVENT      0x00000001
#define ADC1_BUF2_UPDATE_EVENT      0x00000002

#define ADC_VREFINT_VAL             1497.89

#ifdef  BSP_USING_USER_ADC1
 user_adc_config adc1_config =
 {
     .ADC_Handler = ADC1,
     .name        = "user_adc1",
     .RCC_APB2_PERIPH_GPIOX = RCC_APB2_PERIPH_GPIOC,
     .regular_channels = {{ADC_CH_18, RT_NULL, RT_NULL},      //rank1 Vrefint
             {ADC_CH_16, RT_NULL, RT_NULL},      //rank2 temp_cup
             {ADC_CH_6, GPIOC, GPIO_PIN_0},      //rank3 out_voltage
             {ADC_CH_7, GPIOC, GPIO_PIN_1}       //rank4 out_current
     }
 };
 uint16_t user_adc1_val_buf[2][USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH];
 float adc1_ave_buf[USER_ADC1_REGULAR_CH-1];
 rt_thread_t adc_data_handle_thread;
 rt_event_t adc_update_event;
#endif

#ifdef  BSP_USING_USER_ADC2
 user_adc_config adc2_config =
 {
     .ADC_Handler = ADC2,
     .name        = "user_adc2",
 };
#endif

void AdcDataHandleEntry(void *parameter);


rt_err_t user_adc_init(rt_device_t dev)
{
 GPIO_InitType GPIO_InitCtlStruct;
 GPIO_InitStruct(&GPIO_InitCtlStruct);
 RT_ASSERT(dev != RT_NULL);
 ADC_Module *ADCx = (ADC_Module *)dev->user_data;
 ADC_InitType ADC_InitStructure;

 adc_update_event = rt_event_create("adc_update", RT_IPC_FLAG_PRIO);

 if(adc_update_event != RT_NULL)
 {adc_data_handle_thread = rt_thread_create("adc_data_handle", AdcDataHandleEntry, RT_NULL, 2048, 1, 10);
     if(adc_data_handle_thread != RT_NULL)
         rt_thread_startup(adc_data_handle_thread);
 }

 #ifdef BSP_USING_USER_ADC1
 DMA_InitType ADC1_DMA_InitStructure;
 if(ADCx == ADC1)
 {
      /* ADC1 & GPIO clock enable */
      RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, ENABLE);
      ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
      RCC_EnableAPB2PeriphClk(adc1_config.RCC_APB2_PERIPH_GPIOX, ENABLE);

      ADC_InitStruct(&ADC_InitStructure);
      ADC_InitStructure.WorkMode              = ADC_WORKMODE_INDEPENDENT;
      ADC_InitStructure.MultiChEn             = ENABLE;
      ADC_InitStructure.ContinueConvEn        = ENABLE;
      ADC_InitStructure.ExtTrigSelect         = ADC_EXT_TRIGCONV_NONE;
      ADC_InitStructure.DatAlign              = ADC_DAT_ALIGN_R;
      ADC_InitStructure.ChsNumber             = USER_ADC1_REGULAR_CH;
      ADC_Init(ADCx, &ADC_InitStructure);

      /* Configure ADC Channel as analog input */
      for(int i=0; i<USER_ADC1_REGULAR_CH; i++)
      {if(adc1_config.regular_channels[i].channel <= ADC_CH_11)
          {GPIO_InitCtlStruct.Pin = adc1_config.regular_channels[i].GPIO_Pin;
              GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
              GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
              GPIO_InitPeripheral(adc1_config.regular_channels[i].GPIOX, &GPIO_InitCtlStruct);
          }
          /* ADCx regular channels configuration */
          ADC_ConfigRegularChannel(ADCx, adc1_config.regular_channels[i].channel, i+1, ADC_SAMP_TIME_239CYCLES5);

          if ((adc1_config.regular_channels[i].channel == ADC_CH_16) || (adc1_config.regular_channels[i].channel == ADC_CH_18))
          {ADC_EnableTempSensorVrefint(ENABLE);
          }
      }

      /* Enable ADCx */
      ADC_Enable(ADCx, ENABLE);

      /* Start ADCx calibration */
      ADC_StartCalibration(ADCx);
      /* Check the end of ADCx calibration */
      while(ADC_GetCalibrationStatus(ADCx));

      ADC_Enable(ADCx, ENABLE);

      ADC1_DMA_InitStructure.BufSize = USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH * 2;
      ADC1_DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR;
      ADC1_DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
      ADC1_DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
      ADC1_DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
      ADC1_DMA_InitStructure.MemAddr = (uint32_t)user_adc1_val_buf;
      ADC1_DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_HalfWord;
      ADC1_DMA_InitStructure.PeriphAddr = &(ADCx->DAT);
      ADC1_DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
      ADC1_DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
      ADC1_DMA_InitStructure.Priority = DMA_PRIORITY_MEDIUM;
      DMA_Init(DMA1_CH1, &ADC1_DMA_InitStructure);

      DMA_ConfigInt(DMA1_CH1, DMA_INT_HTX | DMA_INT_TXC, ENABLE);

      ADC_EnableDMA(ADCx, ENABLE);

      DMA_EnableChannel(DMA1_CH1, ENABLE);

      NVIC_SetPriorityGrouping(4);
      NVIC_EnableIRQ(DMA1_Channel1_IRQn);

      ADC_EnableSoftwareStartConv(ADCx, ENABLE);

 }
 #endif

 #ifdef BSP_USING_USER_ADC2
 if(ADCx == ADC2)
 {
      /* ADC2 & GPIO clock enable */
      RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2, ENABLE);
      ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
      RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);

      /* Configure ADC Channel as analog input */
      GPIO_InitCtlStruct.Pin = GPIO_PIN_1;
      GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
      GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
      GPIO_InitPeripheral(GPIOC, &GPIO_InitCtlStruct);
 }
 #endif
 return RT_EOK;
}


rt_err_t user_adc_close(rt_device_t dev)
{ADC_Module *ADCx = (ADC_Module *)(dev->user_data);
 ADC_Enable(ADCx, DISABLE);
 if(ADCx == ADC1)
 {DMA_EnableChannel(DMA1_CH1, DISABLE);
     NVIC_DisableIRQ(DMA1_Channel1_IRQn);
     RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, DISABLE);
 }
 rt_thread_delete(adc_data_handle_thread);
 rt_event_delete(adc_update_event);
 dev->flag &= ~(RT_DEVICE_FLAG_ACTIVATED);
 return RT_EOK;
}

static rt_size_t user_adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
 rt_size_t i;
 user_adc_device_t adc = (user_adc_device_t)dev;
 float *value = (float *)buffer;

 for (i = 0; i < size; i++)
 {if(pos+i < USER_ADC1_REGULAR_CH-1)
     {value[pos+i] = adc1_ave_buf[pos+i];
     }
     else {break;}
 }

 return i;
}

static rt_err_t user_adc_control(rt_device_t dev, int cmd, void *args)
{
 rt_err_t result = RT_EOK;
 user_adc_device_t adc = (user_adc_device_t)dev;


 return result;
}

rt_err_t user_hw_adc_register(user_adc_device_t device, const char *name, const void *user_data)
{
 rt_err_t result = RT_EOK;

 device->parent.type = RT_Device_Class_Miscellaneous;
 device->parent.rx_indicate = RT_NULL;
 device->parent.tx_complete = RT_NULL;

 device->parent.init        = user_adc_init;
 device->parent.open        = RT_NULL;
 device->parent.close       = user_adc_close;
 device->parent.read        = user_adc_read;
 device->parent.write       = RT_NULL;
 device->parent.control     = user_adc_control;

 device->parent.user_data = (void *)user_data;

 result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDONLY);

 return result;
}

static int user_hw_adc_init(void)
{
 int result = RT_EOK;
 /* register ADC device */
#ifdef  BSP_USING_USER_ADC1
 if (user_hw_adc_register(&adc1_config.n32_adc_device, adc1_config.name, adc1_config.ADC_Handler) == RT_EOK)
 {LOG_D("%s register success", adc1_config.name);
 }
 else
 {LOG_E("%s register failed", adc1_config.name);
     result = -RT_ERROR;
 }
#endif

#ifdef  BSP_USING_USER_ADC2
 if (user_hw_adc_register(&adc2_config.n32_adc_device, adc2_config.name, adc2_config.ADC_Handler) == RT_EOK)
 {LOG_D("%s register success", adc2_config.name);
 }
 else
 {LOG_E("%s register failed", adc2_config.name);
     result = -RT_ERROR;
 }
#endif

 return result;
}
INIT_COMPONENT_EXPORT(user_hw_adc_init);




void DMA1_Channel1_IRQHandler()
{if(DMA_GetIntStatus(DMA1_INT_HTX1,DMA1) == SET)
 {rt_event_send(adc_update_event, ADC1_BUF1_UPDATE_EVENT);
     DMA_ClrIntPendingBit(DMA1_INT_HTX1, DMA1);
 }
 if(DMA_GetIntStatus(DMA1_INT_TXC1,DMA1) == SET)
 {rt_event_send(adc_update_event, ADC1_BUF2_UPDATE_EVENT);
     DMA_ClrIntPendingBit(DMA1_INT_TXC1, DMA1);
 }
}

void GetAdcDataAverage(float *ave_buf, uint16_t *adc_buf)
{for(int i=0; i<USER_ADC1_REGULAR_CH-1; i++)
 {
     float adc_sum=0;
     for(int j=0; j<USER_ADC1_AVE_N; j++)
     {adc_sum += ADC_VREFINT_VAL * adc_buf[j*USER_ADC1_REGULAR_CH+i+1] / adc_buf[j*USER_ADC1_REGULAR_CH];
     }
     ave_buf[i] = adc_sum / USER_ADC1_AVE_N;
 }
}

void AdcDataHandleEntry(void *parameter)
{
 rt_uint32_t recved_event;
 while(1)
 {if(RT_EOK == rt_event_recv(adc_update_event, ADC1_BUF1_UPDATE_EVENT | ADC1_BUF2_UPDATE_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, -1, &recved_event))
     {if(recved_event & ADC1_BUF1_UPDATE_EVENT)
         {GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[0]);
         }
         if(recved_event & ADC1_BUF2_UPDATE_EVENT)
         {GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[1]);
         }
     }
 }
}

代码的大略思路是通过调用 rt_device_open 函数关上设施时,通过这里的 Init 函数初始化 ADC,开启 DMA。这里用的每通道的 ADC 数据采集量为 100。利用 DMA 的一半传输实现中断实现双 Buffer 的性能,既传输一半实现后,解决前一半数据,齐全传输实现后处理后一半数据。这样数据处理和 DMA 传输能够并行执行。在 DMA 中断中发送对应的 event。开启一个高优先级线程,梗塞期待 DMA 传输实现事件,别离解决对应的数据。这里没有做中值滤波,只做了简略的均匀滤波。测试了一下,采集 ADC 数据的频率大略是 142 帧 /S。也就是大略 7ms 的工夫出一组通过滤波的 ADC 数据。能够依据需要自行批改采样工夫和均匀点数。我这里打算前面做输入电压调节的 PID 线程用 10ms 的,所以这里管制在了 10ms 内。保障每次做 PID 运算都有新数据。

我用的 ADC 的规定组退出了 4 个通道,第一通道是 CH18,既 Vrefint。第二通道是 CH16,测了一下 CPU 温度,最终是要测量整体电路板的温度,依据电路板温度驱动散热风扇调控降温的。最终是用 CPU 温度调控还是在发热器件旁布一个热敏电阻后续再定。这里因为最小零碎板和电源板离开的,CPU 温度也测不到发热器件的温度。所以也只是简略的采集显示了一下。第三通道是 PC0 引脚,用于采集电压输入的反馈。第四通道是 PC1 引脚,用于采集电流反馈。测试板下面没有做采样电阻,而是用的一个霍尔感应器,量程是±10A 的。所以精度有点差。最终也没必要用这个芯片,打算最终做板还是用采样电阻的计划,N32 又自带运放,间接能够放大输出给 ADC 采集。
具体的 ADC 数据处理,是先依据电源纹波在短暂工夫内差别不大的实践下,对每一次循环采集的 4 通道数据,依据外部基准电压做了个弥补。这样前面依据 ADC 数据计算的电压才是精确的。我这里实测的数据如下图:

数组的 0,4,8,12… 就是保留的实时采集的 Vrefint 的 ADC 数据,而此时的 VrefP 的供电电压是 3.265V。能够计算失去 Vrefint 的理论电压是 1.2068。如下图是官网给的 Vrefint 的电压范畴。

依据理论的 Vrefint 能够推算出 VrefP 供电电压为规范的 3.3V 供电时,Vrefint 的 ADC 转换值为 1497.89。我看默认配置曾经开启了 FPU,所以这里间接用浮点数了,能够进步一点精度。

如下是 main.c 下实现的具体利用代码。比较简单,创立了一个电压控制线程,因为电压调控对于 10ms 的管制周期简直没有滞后性,所以这里的 PID 管制,其实只用了 PI。

#define LED1_PIN    90
#define LED2_PIN    91
#define LED3_PIN    67

#define CPU_Avg_Slope   -4.1
#define CPU_V25         1.33

uint16_t test_val=0;
uint8_t print_buf[128];

float read_adc_buf[USER_ADC1_REGULAR_CH-1];

rt_device_t OpenADCDevice(char *name);
rt_device_t adc1_dev=RT_NULL;
rt_device_t dac_dev=RT_NULL;

rt_tick_t shell_getchr_tick;
rt_thread_t voltage_ctrl_thread;
void VoltageCtrlEntry(void *parameter);

void plot_ui()
{rt_kprintf("\033[2J");
    rt_kprintf("\033[10;0H");
    rt_kprintf("*******************************************\r\n");
    rt_kprintf("*         voltage:");
    rt_kprintf("\033[1;40;31m%s\033[0m","00.00");
    rt_kprintf("V           *\r\n");
    rt_kprintf("*         current:");
    rt_kprintf("\033[1;40;32m%s\033[0m","0.000");
    rt_kprintf("A           *\r\n");
    rt_kprintf("*         power:");
    rt_kprintf("\033[1;40;34m%s\033[0m","00.00");
    rt_kprintf("W           *\r\n");
    rt_kprintf("*         CPUTemp:   00.00    C           *\r\n");
    rt_kprintf("*******************************************\r\n");
    rt_kprintf("\033[20;0H");
}

int main(void)
{
    rt_err_t result;
    uint32_t Speed = 500;
    /* set LED1 pin mode to output */
    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(LED3_PIN, PIN_MODE_OUTPUT);


    adc1_dev = OpenADCDevice("user_adc1");

    voltage_ctrl_thread = rt_thread_create("VoltageCtrl", VoltageCtrlEntry, RT_NULL, 2048, 5, 10);
    if(voltage_ctrl_thread != RT_NULL)
        rt_thread_startup(voltage_ctrl_thread);

    plot_ui();


    while (1)
    {rt_pin_write(LED1_PIN, PIN_LOW);
        rt_pin_write(LED2_PIN, PIN_LOW);
        rt_pin_write(LED3_PIN, PIN_LOW);
        rt_thread_mdelay(Speed);
        rt_pin_write(LED1_PIN, PIN_HIGH);
        rt_pin_write(LED2_PIN, PIN_HIGH);
        rt_pin_write(LED3_PIN, PIN_HIGH);
        rt_thread_mdelay(Speed);



    }
}

rt_device_t OpenADCDevice(char *name)
{
    rt_err_t result;
    rt_device_t adc_dev = rt_device_find(name);
    if(adc_dev != RT_NULL)
    {if(adc_dev->open_flag == RT_DEVICE_OFLAG_CLOSE)
        {result = rt_device_open(adc_dev, RT_DEVICE_OFLAG_RDONLY);
            if(result == RT_EOK)
            {rt_kprintf("%s opened!\r\n",name);
            }
            else {rt_kprintf("%s open err:%d!\r\n",name,result);
            }
        }
        else {rt_kprintf("%s is already opened!\r\n",name);
        }
    }
    else {rt_kprintf("not find %s device!\r\n",name);
    }
    return adc_dev;
}
void OpenADC1Device()
{adc1_dev = OpenADCDevice("user_adc1");
}
MSH_CMD_EXPORT(OpenADC1Device, open adc1 device and start adc1 conversion);

rt_err_t CloseADCDevice(char *name)
{
    rt_err_t result;
    rt_device_t adc_dev = rt_device_find(name);
    if(adc_dev != RT_NULL)
    {result = rt_device_close(adc_dev);
        if(result == RT_EOK)
        {rt_kprintf("%s closed!\r\n",name);
        }
        else {rt_kprintf("%s close err:%d!\r\n",name,result);
        }
    }
    else {rt_kprintf("not find %s device!\r\n",name);
    }
    return result;
}
void CloseADC1Device()
{CloseADCDevice("user_adc1");
}
MSH_CMD_EXPORT(CloseADC1Device, close adc1 device and stop adc1 conversion);

void ReadADC1Val()
{if(adc1_dev != RT_NULL && adc1_dev->open_flag != RT_DEVICE_OFLAG_CLOSE)
    {rt_device_read(adc1_dev, 0, read_adc_buf, USER_ADC1_REGULAR_CH-1);

        sprintf(print_buf, "ADC:%f,%f,%f,%f\r\n", read_adc_buf[0], read_adc_buf[1], read_adc_buf[2], read_adc_buf[3]);
        rt_kprintf(print_buf);
    }
    else {rt_kprintf("user_adc1 dev not opened!\r\n");
    }
}
MSH_CMD_EXPORT(ReadADC1Val, read adc1 all channel val);


void get_shell_getchr_tick()
{shell_getchr_tick = rt_tick_get();
}

float SetVoltageVal = 5.0;      //V

float SetCurrentVal = 3.0;         //A

float VoltagePID_P = 100;
float VoltagePID_I = 50;

float user_atof(char *str)
{
    float val;
    char *ch_p=RT_NULL;
    do{if(*str == 0)
            return 0;
        if(*str == ' ')
        {str++;}
        else {break;}
    }
    while(1);
    ch_p = strchr(str , '.');
    int valH = atoi(str);
    float valL=0;
    if(ch_p > 0)
        valL = atoi(ch_p+1);
    do{if(valL >= 1)
        {valL /= 10;}
        else {break;}
    }while(1);
    val = valH + valL;
    return val;
}

float Sin_vpp=0, Sin_offset=0, Sin_cycle=0, Singal_Cycle_step=0;
void EnSinOut(int argc, char **argv)
{if(argc >= 2)
    {if(*argv[1] == '?')
        {sprintf(print_buf,"Vpp:%f, Offset:%f, Cycle:%f\r\n", Sin_vpp, Sin_offset, Sin_cycle);
            rt_kprintf(print_buf);
        }
        else if(argc >= 4)
        {float val = user_atof(argv[1]);
            if(val >=0 && val <=35)
            {Sin_vpp = val;}
            else {sprintf(print_buf,"Error:Out range[0,35]! Vpp:%fV\r\n", Sin_vpp);
                rt_kprintf(print_buf);
            }
            val = user_atof(argv[2]);
            if(val >=0 && val <=35)
            {Sin_offset = val;}
            else {sprintf(print_buf,"Error:Out range[0,35]! Offset:%fV\r\n", Sin_offset);
                rt_kprintf(print_buf);
            }
            val = user_atof(argv[3]);
            if(val >=1 && val <=100)
            {
                Sin_cycle = val;
                Singal_Cycle_step = 2*3.1415926/100/Sin_cycle;
            }
            else {sprintf(print_buf,"Error:Out range[1,100]! Cycle:%fS\r\n", Sin_cycle);
                rt_kprintf(print_buf);
            }
            sprintf(print_buf,"Vpp:%fV, Offset:%fV, Cycle:%fS\r\n", Sin_vpp, Sin_offset, Sin_cycle);
            rt_kprintf(print_buf);
        }
        else {rt_kprintf("EnSinOut [vpp offset cycle]\r\n");
        }
    }
    else {rt_kprintf("EnSinOut [vpp offset cycle]\r\n");
    }
}
MSH_CMD_EXPORT(EnSinOut, enable sinusoidal signal out);

void SetVoltage(int argc, char **argv)
{if(argc >= 2)
    {if(*argv[1] == '?')
        {sprintf(print_buf,"SetVoltageVal:%f\r\n", SetVoltageVal);
            rt_kprintf(print_buf);
        }
        else {float val = user_atof(argv[1]);
            if(val >=0 && val <=35)
            {
                Sin_vpp = 0;
                SetVoltageVal = val;
                sprintf(print_buf,"SetVoltageVal:%f\r\n", SetVoltageVal);
                rt_kprintf(print_buf);
            }
            else {sprintf(print_buf,"Error:Out range[0,35]! SetVoltageVal:%f\r\n", SetVoltageVal);
                rt_kprintf(print_buf);
            }
        }
    }
    else {rt_kprintf("SetVoltage [val]\r\n");
    }
}
MSH_CMD_EXPORT(SetVoltage, set voltage val);

void SetCurrent(int argc, char **argv)
{if(argc >= 2)
    {if(*argv[1] == '?')
        {sprintf(print_buf,"SetCurrentVal:%f\r\n", SetCurrentVal);
            rt_kprintf(print_buf);
        }
        else {float val = user_atof(argv[1]);
            if(val >=0 && val <=5)
            {
                SetCurrentVal = val;
                sprintf(print_buf,"SetCurrentVal:%f\r\n", SetCurrentVal);
                rt_kprintf(print_buf);
            }
            else {sprintf(print_buf,"Error:Out range[0,5]! SetCurrentVal:%f\r\n", SetCurrentVal);
                rt_kprintf(print_buf);
            }
        }
    }
    else {rt_kprintf("SetCurrent [val]\r\n");
    }
}
MSH_CMD_EXPORT(SetCurrent, set current val);


void VoltageCtrlEntry(void *parameter)
{
    uint8_t wait_i=0, replot_flag=0, overcurrent_flag=0;
    float cpu_temp_ave=0, voltage_ave=0, current_ave=0;
    dac_dev = rt_device_find("dac");
    uint32_t ch = 1;
    int32_t dac_val = 4095;
    rt_device_control(dac_dev, RT_DAC_CMD_ENABLE, &ch);
    rt_device_open(dac_dev, RT_DEVICE_OFLAG_RDWR);
    rt_device_write(dac_dev, ch, &dac_val, 1);

    float Voltage_err=0, Voltage_err_old=0, Voltage_err_sum=0;

    float CtrlVoltage=0;

    float Singal_Cycle_i=0;

    rt_thread_sleep(100);
    while(1)
    {if(adc1_dev != RT_NULL && adc1_dev->open_flag != RT_DEVICE_OFLAG_CLOSE)
        {rt_device_read(adc1_dev, 0, read_adc_buf, USER_ADC1_REGULAR_CH-1);
            float cpu_temp = (CPU_V25 - read_adc_buf[0] * 3.3 / 4096) * 1000 / CPU_Avg_Slope + 25;
            float voltage = read_adc_buf[1] * 3.3 / 4096 * 12;
            float current = -(read_adc_buf[2] * 3.3 / 4096 - 1.65)/0.132;
            if(current < 0)
                current = 0;

            if(Sin_vpp > 0.0001)        // 使能正弦信号输入
            {Singal_Cycle_i = fmod(Singal_Cycle_i + Singal_Cycle_step, 2*3.1415926);
                SetVoltageVal = Sin_offset + Sin_vpp / 2 * sin(Singal_Cycle_i);
            }

            if(current <= SetCurrentVal)
            {if(overcurrent_flag)
                {if(CtrlVoltage < SetVoltageVal-0.1)
                    {CtrlVoltage += 0.1;}
                    else if(CtrlVoltage > SetVoltageVal+0.1)
                    {CtrlVoltage -= 0.1;}
                    else {
                        CtrlVoltage = SetVoltageVal;
                        overcurrent_flag = 0;
                    }
                }
                else {CtrlVoltage = SetVoltageVal;}
            }
            else {
                overcurrent_flag = 1;
                CtrlVoltage -= 0.1;
            }

            Voltage_err = voltage - CtrlVoltage;
            if(Voltage_err < 0.1 && Voltage_err > -0.1)
            {Voltage_err_sum += Voltage_err;}

            if(Voltage_err_old * Voltage_err < 0)
            {Voltage_err_sum = 0;}

            if(Voltage_err_sum > 2)
            {Voltage_err_sum = 2;}
            else if(Voltage_err_sum < -2)
            {Voltage_err_sum = -2;}

            Voltage_err_old = Voltage_err;

            dac_val += VoltagePID_P * Voltage_err + VoltagePID_I * Voltage_err_sum;
            if(dac_val < 0)
            {dac_val = 0;}
            else if(dac_val > 4095)
            {dac_val = 4095;}
            rt_device_write(dac_dev, ch, &dac_val, 1);

            cpu_temp_ave += cpu_temp;
            voltage_ave += voltage;
            current_ave += current;

            if(wait_i++ >= 100)
            {if(rt_tick_get() - shell_getchr_tick > 5000)
                {if(replot_flag)
                    {plot_ui();
                        replot_flag = 0;
                    }
                    sprintf(print_buf,"%.2f",voltage_ave / 101);
                    rt_kprintf("\033[11;26H");
                    rt_kprintf("\033[1;40;31m%s\033[0m",print_buf);

                    sprintf(print_buf,"%.3f",current_ave / 101);
                    rt_kprintf("\033[12;26H");
                    rt_kprintf("\033[1;40;32m%s\033[0m",print_buf);

                    sprintf(print_buf,"%.3f",voltage_ave / 101 * current_ave / 101);
                    rt_kprintf("\033[13;26H");
                    rt_kprintf("\033[1;40;34m%s\033[0m",print_buf);

                    sprintf(print_buf,"%.2f",cpu_temp_ave / 101);
                    rt_kprintf("\033[14;26H");
                    rt_kprintf(print_buf);
                    rt_kprintf("\033[20;0H");
                }
                else {replot_flag = 1;}
                wait_i = 0;
                cpu_temp_ave = 0;
                voltage_ave = 0;
                current_ave = 0;
            }
        }
        rt_thread_sleep(RT_TICK_PER_SECOND/100);
    }
}

具体代码,有趣味的敌人能够看一下,我就不一点点介绍剖析了。测试用的,代码写的也比拟随便,也没做正文。心愿能对有须要的敌人提供帮忙。也能够在此基础上再持续优化欠缺代码。前面等上传了演示视频再附上视频地址。祝大家工作学习开心欢快。

退出移动版