[N32G45x] 基于RT-Thread和N32G457的简易便捷式可调电压源

[复制链接]
2986|0
 楼主| 安小芯 发表于 2022-4-14 18:25 | 显示全部楼层 |阅读模式
本帖最后由 安小芯 于 2022-4-14 18:30 编辑


基于RT-Thread和N32G457的简易便捷式可调电压源
作品出处:RT-Thread
作者:吉利咕噜
本设计是用来参加《创新“芯”引擎 | 国民技术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数据的代码:
  1. <font face="宋体" size="4">#define ADC1_BUF1_UPDATE_EVENT      0x00000001
  2. #define ADC1_BUF2_UPDATE_EVENT      0x00000002<p></p>
  3. <p style="line-height: 30px; text-indent: 2em;">#define ADC_VREFINT_VAL             1497.89</p>
  4. <p style="line-height: 30px; text-indent: 2em;">#ifdef  BSP_USING_USER_ADC1
  5. user_adc_config adc1_config =
  6. {
  7.      .ADC_Handler = ADC1,
  8.      .name        = "user_adc1",
  9.      .RCC_APB2_PERIPH_GPIOX = RCC_APB2_PERIPH_GPIOC,
  10.      .regular_channels = {
  11.              {ADC_CH_18, RT_NULL, RT_NULL},      //rank1 Vrefint
  12.              {ADC_CH_16, RT_NULL, RT_NULL},      //rank2 temp_cup
  13.              {ADC_CH_6, GPIOC, GPIO_PIN_0},      //rank3 out_voltage
  14.              {ADC_CH_7, GPIOC, GPIO_PIN_1}       //rank4 out_current
  15.      }
  16. };
  17. uint16_t user_adc1_val_buf[2][USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH];
  18. float adc1_ave_buf[USER_ADC1_REGULAR_CH-1];
  19. rt_thread_t adc_data_handle_thread;
  20. rt_event_t adc_update_event;
  21. #endif</p>
  22. <p style="line-height: 30px; text-indent: 2em;">#ifdef  BSP_USING_USER_ADC2
  23. user_adc_config adc2_config =
  24. {
  25.      .ADC_Handler = ADC2,
  26.      .name        = "user_adc2",
  27. };
  28. #endif</p>
  29. <p style="line-height: 30px; text-indent: 2em;">void AdcDataHandleEntry(void *parameter);</p>
  30. <p style="line-height: 30px; text-indent: 2em;">rt_err_t user_adc_init(rt_device_t dev)
  31. {
  32. GPIO_InitType GPIO_InitCtlStruct;
  33. GPIO_InitStruct(&GPIO_InitCtlStruct);
  34. RT_ASSERT(dev != RT_NULL);
  35. ADC_Module *ADCx = (ADC_Module *)dev->user_data;
  36. ADC_InitType ADC_InitStructure;</p>
  37. <p style="line-height: 30px; text-indent: 2em;"> adc_update_event = rt_event_create("adc_update", RT_IPC_FLAG_PRIO);</p>
  38. <p style="line-height: 30px; text-indent: 2em;"> if(adc_update_event != RT_NULL)
  39. {
  40.      adc_data_handle_thread = rt_thread_create("adc_data_handle", AdcDataHandleEntry, RT_NULL, 2048, 1, 10);
  41.      if(adc_data_handle_thread != RT_NULL)
  42.          rt_thread_startup(adc_data_handle_thread);
  43. }</p>
  44. <p style="line-height: 30px; text-indent: 2em;"> #ifdef BSP_USING_USER_ADC1
  45. DMA_InitType ADC1_DMA_InitStructure;
  46. if(ADCx == ADC1)
  47. {
  48.       /* ADC1 & GPIO clock enable */
  49.       RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, ENABLE);
  50.       ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
  51.       RCC_EnableAPB2PeriphClk(adc1_config.RCC_APB2_PERIPH_GPIOX, ENABLE);</p>
  52. <p style="line-height: 30px; text-indent: 2em;">      ADC_InitStruct(&ADC_InitStructure);
  53.       ADC_InitStructure.WorkMode              = ADC_WORKMODE_INDEPENDENT;
  54.       ADC_InitStructure.MultiChEn             = ENABLE;
  55.       ADC_InitStructure.ContinueConvEn        = ENABLE;
  56.       ADC_InitStructure.ExtTrigSelect         = ADC_EXT_TRIGCONV_NONE;
  57.       ADC_InitStructure.DatAlign              = ADC_DAT_ALIGN_R;
  58.       ADC_InitStructure.ChsNumber             = USER_ADC1_REGULAR_CH;
  59.       ADC_Init(ADCx, &ADC_InitStructure);</p>
  60. <p style="line-height: 30px; text-indent: 2em;">      /* Configure ADC Channel as analog input */
  61.       for(int i=0; i<USER_ADC1_REGULAR_CH; i++)
  62.       {
  63.           if(adc1_config.regular_channels[i].channel <= ADC_CH_11)
  64.           {
  65.               GPIO_InitCtlStruct.Pin = adc1_config.regular_channels[i].GPIO_Pin;
  66.               GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
  67.               GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
  68.               GPIO_InitPeripheral(adc1_config.regular_channels[i].GPIOX, &GPIO_InitCtlStruct);
  69.           }
  70.           /* ADCx regular channels configuration */
  71.           ADC_ConfigRegularChannel(ADCx, adc1_config.regular_channels[i].channel, i+1, ADC_SAMP_TIME_239CYCLES5);</p>
  72. <p style="line-height: 30px; text-indent: 2em;">          if ((adc1_config.regular_channels[i].channel == ADC_CH_16) || (adc1_config.regular_channels[i].channel == ADC_CH_18))
  73.           {
  74.               ADC_EnableTempSensorVrefint(ENABLE);
  75.           }
  76.       }</p>
  77. <p style="line-height: 30px; text-indent: 2em;">      /* Enable ADCx */
  78.       ADC_Enable(ADCx, ENABLE);</p>
  79. <p style="line-height: 30px; text-indent: 2em;">      /* Start ADCx calibration */
  80.       ADC_StartCalibration(ADCx);
  81.       /* Check the end of ADCx calibration */
  82.       while(ADC_GetCalibrationStatus(ADCx));</p>
  83. <p style="line-height: 30px; text-indent: 2em;">      ADC_Enable(ADCx, ENABLE);</p>
  84. <p style="line-height: 30px; text-indent: 2em;">      ADC1_DMA_InitStructure.BufSize = USER_ADC1_AVE_N * USER_ADC1_REGULAR_CH * 2;
  85.       ADC1_DMA_InitStructure.CircularMode = DMA_MODE_CIRCULAR;
  86.       ADC1_DMA_InitStructure.DMA_MemoryInc = DMA_MEM_INC_ENABLE;
  87.       ADC1_DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC;
  88.       ADC1_DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE;
  89.       ADC1_DMA_InitStructure.MemAddr = (uint32_t)user_adc1_val_buf;
  90.       ADC1_DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_HalfWord;
  91.       ADC1_DMA_InitStructure.PeriphAddr = &(ADCx->DAT);
  92.       ADC1_DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_HALFWORD;
  93.       ADC1_DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE;
  94.       ADC1_DMA_InitStructure.Priority = DMA_PRIORITY_MEDIUM;
  95.       DMA_Init(DMA1_CH1, &ADC1_DMA_InitStructure);</p>
  96. <p style="line-height: 30px; text-indent: 2em;">      DMA_ConfigInt(DMA1_CH1, DMA_INT_HTX | DMA_INT_TXC, ENABLE);</p>
  97. <p style="line-height: 30px; text-indent: 2em;">      ADC_EnableDMA(ADCx, ENABLE);</p>
  98. <p style="line-height: 30px; text-indent: 2em;">      DMA_EnableChannel(DMA1_CH1, ENABLE);</p>
  99. <p style="line-height: 30px; text-indent: 2em;">      NVIC_SetPriorityGrouping(4);
  100.       NVIC_EnableIRQ(DMA1_Channel1_IRQn);</p>
  101. <p style="line-height: 30px; text-indent: 2em;">      ADC_EnableSoftwareStartConv(ADCx, ENABLE);</p>
  102. <p style="line-height: 30px; text-indent: 2em;"> }
  103. #endif</p>
  104. <p style="line-height: 30px; text-indent: 2em;"> #ifdef BSP_USING_USER_ADC2
  105. if(ADCx == ADC2)
  106. {
  107.       /* ADC2 & GPIO clock enable */
  108.       RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC2, ENABLE);
  109.       ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB,RCC_ADCHCLK_DIV8);
  110.       RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOC, ENABLE);</p>
  111. <p style="line-height: 30px; text-indent: 2em;">      /* Configure ADC Channel as analog input */
  112.       GPIO_InitCtlStruct.Pin = GPIO_PIN_1;
  113.       GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_2MHz;
  114.       GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AIN;
  115.       GPIO_InitPeripheral(GPIOC, &GPIO_InitCtlStruct);
  116. }
  117. #endif
  118. return RT_EOK;
  119. }</p>
  120. <p style="line-height: 30px; text-indent: 2em;">rt_err_t user_adc_close(rt_device_t dev)
  121. {
  122. ADC_Module *ADCx = (ADC_Module *)(dev->user_data);
  123. ADC_Enable(ADCx, DISABLE);
  124. if(ADCx == ADC1)
  125. {
  126.      DMA_EnableChannel(DMA1_CH1, DISABLE);
  127.      NVIC_DisableIRQ(DMA1_Channel1_IRQn);
  128.      RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC1 | RCC_AHB_PERIPH_DMA1, DISABLE);
  129. }
  130. rt_thread_delete(adc_data_handle_thread);
  131. rt_event_delete(adc_update_event);
  132. dev->flag &= ~(RT_DEVICE_FLAG_ACTIVATED);
  133. return RT_EOK;
  134. }</p>
  135. <p style="line-height: 30px; text-indent: 2em;">static rt_size_t user_adc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
  136. {
  137. rt_size_t i;
  138. user_adc_device_t adc = (user_adc_device_t)dev;
  139. float *value = (float *)buffer;</p>
  140. <p style="line-height: 30px; text-indent: 2em;"> for (i = 0; i < size; i++)
  141. {
  142.      if(pos+i < USER_ADC1_REGULAR_CH-1)
  143.      {
  144.          value[pos+i] = adc1_ave_buf[pos+i];
  145.      }
  146.      else {
  147.          break;
  148.      }
  149. }</p>
  150. <p style="line-height: 30px; text-indent: 2em;"> return i;
  151. }</p>
  152. <p style="line-height: 30px; text-indent: 2em;">static rt_err_t user_adc_control(rt_device_t dev, int cmd, void *args)
  153. {
  154. rt_err_t result = RT_EOK;
  155. user_adc_device_t adc = (user_adc_device_t)dev;</p>
  156. <p style="line-height: 30px; text-indent: 2em;"> return result;
  157. }</p>
  158. <p style="line-height: 30px; text-indent: 2em;">rt_err_t user_hw_adc_register(user_adc_device_t device, const char *name, const void *user_data)
  159. {
  160. rt_err_t result = RT_EOK;</p>
  161. <p style="line-height: 30px; text-indent: 2em;"> device->parent.type = RT_Device_Class_Miscellaneous;
  162. device->parent.rx_indicate = RT_NULL;
  163. device->parent.tx_complete = RT_NULL;</p>
  164. <p style="line-height: 30px; text-indent: 2em;"> device->parent.init        = user_adc_init;
  165. device->parent.open        = RT_NULL;
  166. device->parent.close       = user_adc_close;
  167. device->parent.read        = user_adc_read;
  168. device->parent.write       = RT_NULL;
  169. device->parent.control     = user_adc_control;</p>
  170. <p style="line-height: 30px; text-indent: 2em;"> device->parent.user_data = (void *)user_data;</p>
  171. <p style="line-height: 30px; text-indent: 2em;"> result = rt_device_register(&device->parent, name, RT_DEVICE_FLAG_RDONLY);</p>
  172. <p style="line-height: 30px; text-indent: 2em;"> return result;
  173. }</p>
  174. <p style="line-height: 30px; text-indent: 2em;">static int user_hw_adc_init(void)
  175. {
  176. int result = RT_EOK;
  177. /* register ADC device */
  178. #ifdef  BSP_USING_USER_ADC1
  179. if (user_hw_adc_register(&adc1_config.n32_adc_device, adc1_config.name, adc1_config.ADC_Handler) == RT_EOK)
  180. {
  181.      LOG_D("%s register success", adc1_config.name);
  182. }
  183. else
  184. {
  185.      LOG_E("%s register failed", adc1_config.name);
  186.      result = -RT_ERROR;
  187. }
  188. #endif</p>
  189. <p style="line-height: 30px; text-indent: 2em;">#ifdef  BSP_USING_USER_ADC2
  190. if (user_hw_adc_register(&adc2_config.n32_adc_device, adc2_config.name, adc2_config.ADC_Handler) == RT_EOK)
  191. {
  192.      LOG_D("%s register success", adc2_config.name);
  193. }
  194. else
  195. {
  196.      LOG_E("%s register failed", adc2_config.name);
  197.      result = -RT_ERROR;
  198. }
  199. #endif</p>
  200. <p style="line-height: 30px; text-indent: 2em;"> return result;
  201. }
  202. INIT_COMPONENT_EXPORT(user_hw_adc_init);</p>
  203. <p style="line-height: 30px; text-indent: 2em;">void DMA1_Channel1_IRQHandler()
  204. {
  205. if(DMA_GetIntStatus(DMA1_INT_HTX1,DMA1) == SET)
  206. {
  207.      rt_event_send(adc_update_event, ADC1_BUF1_UPDATE_EVENT);
  208.      DMA_ClrIntPendingBit(DMA1_INT_HTX1, DMA1);
  209. }
  210. if(DMA_GetIntStatus(DMA1_INT_TXC1,DMA1) == SET)
  211. {
  212.      rt_event_send(adc_update_event, ADC1_BUF2_UPDATE_EVENT);
  213.      DMA_ClrIntPendingBit(DMA1_INT_TXC1, DMA1);
  214. }
  215. }</p>
  216. <p style="line-height: 30px; text-indent: 2em;">void GetAdcDataAverage(float *ave_buf, uint16_t *adc_buf)
  217. {
  218. for(int i=0; i<USER_ADC1_REGULAR_CH-1; i++)
  219. {
  220.      float adc_sum=0;
  221.      for(int j=0; j<USER_ADC1_AVE_N; j++)
  222.      {
  223.          adc_sum += ADC_VREFINT_VAL * adc_buf[j*USER_ADC1_REGULAR_CH+i+1] / adc_buf[j*USER_ADC1_REGULAR_CH];
  224.      }
  225.      ave_buf[i] = adc_sum / USER_ADC1_AVE_N;
  226. }
  227. }</p>
  228. </font><p style="line-height: 30px; text-indent: 2em;"><font face="宋体" size="4">void AdcDataHandleEntry(void *parameter)
  229. {
  230. rt_uint32_t recved_event;
  231. while(1)
  232. {
  233.      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))
  234.      {
  235.          if(recved_event & ADC1_BUF1_UPDATE_EVENT)
  236.          {
  237.              GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[0]);
  238.          }
  239.          if(recved_event & ADC1_BUF2_UPDATE_EVENT)
  240.          {
  241.              GetAdcDataAverage(adc1_ave_buf, user_adc1_val_buf[1]);
  242.          }
  243.      }
  244. }
  245. }</font></p>
代码的大概思路是通过调用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。

  1. <font face="宋体" size="4">#define LED1_PIN    90
  2. #define LED2_PIN    91
  3. #define LED3_PIN    67<p></p>
  4. <p style="line-height: 30px; text-indent: 2em;">#define CPU_Avg_Slope   -4.1
  5. #define CPU_V25         1.33</p>
  6. <p style="line-height: 30px; text-indent: 2em;">uint16_t test_val=0;
  7. uint8_t print_buf[128];</p>
  8. <p style="line-height: 30px; text-indent: 2em;">float read_adc_buf[USER_ADC1_REGULAR_CH-1];</p>
  9. <p style="line-height: 30px; text-indent: 2em;">rt_device_t OpenADCDevice(char *name);
  10. rt_device_t adc1_dev=RT_NULL;
  11. rt_device_t dac_dev=RT_NULL;</p>
  12. <p style="line-height: 30px; text-indent: 2em;">rt_tick_t shell_getchr_tick;
  13. rt_thread_t voltage_ctrl_thread;
  14. void VoltageCtrlEntry(void *parameter);</p>
  15. <p style="line-height: 30px; text-indent: 2em;">void plot_ui()
  16. {
  17.     rt_kprintf("\033[2J");
  18.     rt_kprintf("\033[10;0H");
  19.     rt_kprintf("    *******************************************\r\n");
  20.     rt_kprintf("    *         voltage:");
  21.     rt_kprintf("\033[1;40;31m%s\033[0m","   00.00");
  22.     rt_kprintf("    V           *\r\n");
  23.     rt_kprintf("    *         current:");
  24.     rt_kprintf("\033[1;40;32m%s\033[0m","   0.000");
  25.     rt_kprintf("    A           *\r\n");
  26.     rt_kprintf("    *         power:  ");
  27.     rt_kprintf("\033[1;40;34m%s\033[0m","   00.00");
  28.     rt_kprintf("    W           *\r\n");
  29.     rt_kprintf("    *         CPUTemp:   00.00    C           *\r\n");
  30.     rt_kprintf("    *******************************************\r\n");
  31.     rt_kprintf("\033[20;0H");
  32. }</p>
  33. <p style="line-height: 30px; text-indent: 2em;">int main(void)
  34. {
  35.     rt_err_t result;
  36.     uint32_t Speed = 500;
  37.     /* set LED1 pin mode to output */
  38.     rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
  39.     rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
  40.     rt_pin_mode(LED3_PIN, PIN_MODE_OUTPUT);</p>
  41. <p style="line-height: 30px; text-indent: 2em;">    adc1_dev = OpenADCDevice("user_adc1");</p>
  42. <p style="line-height: 30px; text-indent: 2em;">    voltage_ctrl_thread = rt_thread_create("VoltageCtrl", VoltageCtrlEntry, RT_NULL, 2048, 5, 10);
  43.     if(voltage_ctrl_thread != RT_NULL)
  44.         rt_thread_startup(voltage_ctrl_thread);</p>
  45. <p style="line-height: 30px; text-indent: 2em;">    plot_ui();</p>
  46. <p style="line-height: 30px; text-indent: 2em;">    while (1)
  47.     {
  48.         rt_pin_write(LED1_PIN, PIN_LOW);
  49.         rt_pin_write(LED2_PIN, PIN_LOW);
  50.         rt_pin_write(LED3_PIN, PIN_LOW);
  51.         rt_thread_mdelay(Speed);
  52.         rt_pin_write(LED1_PIN, PIN_HIGH);
  53.         rt_pin_write(LED2_PIN, PIN_HIGH);
  54.         rt_pin_write(LED3_PIN, PIN_HIGH);
  55.         rt_thread_mdelay(Speed);</p>
  56. <p style="line-height: 30px; text-indent: 2em;">    }
  57. }</p>
  58. <p style="line-height: 30px; text-indent: 2em;">rt_device_t OpenADCDevice(char *name)
  59. {
  60.     rt_err_t result;
  61.     rt_device_t adc_dev = rt_device_find(name);
  62.     if(adc_dev != RT_NULL)
  63.     {
  64.         if(adc_dev->open_flag == RT_DEVICE_OFLAG_CLOSE)
  65.         {
  66.             result = rt_device_open(adc_dev, RT_DEVICE_OFLAG_RDONLY);
  67.             if(result == RT_EOK)
  68.             {
  69.                 rt_kprintf("%s opened!\r\n",name);
  70.             }
  71.             else {
  72.                 rt_kprintf("%s open err:%d!\r\n",name,result);
  73.             }
  74.         }
  75.         else {
  76.             rt_kprintf("%s is already opened!\r\n",name);
  77.         }
  78.     }
  79.     else {
  80.         rt_kprintf("not find %s device!\r\n",name);
  81.     }
  82.     return adc_dev;
  83. }
  84. void OpenADC1Device()
  85. {
  86.     adc1_dev = OpenADCDevice("user_adc1");
  87. }
  88. MSH_CMD_EXPORT(OpenADC1Device, open adc1 device and start adc1 conversion);</p>
  89. <p style="line-height: 30px; text-indent: 2em;">rt_err_t CloseADCDevice(char *name)
  90. {
  91.     rt_err_t result;
  92.     rt_device_t adc_dev = rt_device_find(name);
  93.     if(adc_dev != RT_NULL)
  94.     {
  95.         result = rt_device_close(adc_dev);
  96.         if(result == RT_EOK)
  97.         {
  98.             rt_kprintf("%s closed!\r\n",name);
  99.         }
  100.         else {
  101.             rt_kprintf("%s close err:%d!\r\n",name,result);
  102.         }
  103.     }
  104.     else {
  105.         rt_kprintf("not find %s device!\r\n",name);
  106.     }
  107.     return result;
  108. }
  109. void CloseADC1Device()
  110. {
  111.     CloseADCDevice("user_adc1");
  112. }
  113. MSH_CMD_EXPORT(CloseADC1Device, close adc1 device and stop adc1 conversion);</p>
  114. <p style="line-height: 30px; text-indent: 2em;">void ReadADC1Val()
  115. {
  116.     if(adc1_dev != RT_NULL && adc1_dev->open_flag != RT_DEVICE_OFLAG_CLOSE)
  117.     {
  118.         rt_device_read(adc1_dev, 0, read_adc_buf, USER_ADC1_REGULAR_CH-1);</p>
  119. <p style="line-height: 30px; text-indent: 2em;">        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]);
  120.         rt_kprintf(print_buf);
  121.     }
  122.     else {
  123.         rt_kprintf("user_adc1 dev not opened!\r\n");
  124.     }
  125. }
  126. MSH_CMD_EXPORT(ReadADC1Val, read adc1 all channel val);</p>
  127. <p style="line-height: 30px; text-indent: 2em;">void get_shell_getchr_tick()
  128. {
  129.     shell_getchr_tick = rt_tick_get();
  130. }</p>
  131. <p style="line-height: 30px; text-indent: 2em;">float SetVoltageVal = 5.0;      //V</p>
  132. <p style="line-height: 30px; text-indent: 2em;">float SetCurrentVal = 3.0;         //A</p>
  133. <p style="line-height: 30px; text-indent: 2em;">float VoltagePID_P = 100;
  134. float VoltagePID_I = 50;</p>
  135. <p style="line-height: 30px; text-indent: 2em;">float user_atof(char *str)
  136. {
  137.     float val;
  138.     char *ch_p=RT_NULL;
  139.     do{
  140.         if(*str == 0)
  141.             return 0;
  142.         if(*str == ' ')
  143.         {
  144.             str++;
  145.         }
  146.         else {
  147.             break;
  148.         }
  149.     }
  150.     while(1);
  151.     ch_p = strchr(str , '.');
  152.     int valH = atoi(str);
  153.     float valL=0;
  154.     if(ch_p > 0)
  155.         valL = atoi(ch_p+1);
  156.     do{
  157.         if(valL >= 1)
  158.         {
  159.             valL /= 10;
  160.         }
  161.         else {
  162.             break;
  163.         }
  164.     }while(1);
  165.     val = valH + valL;
  166.     return val;
  167. }</p>
  168. <p style="line-height: 30px; text-indent: 2em;">float Sin_vpp=0, Sin_offset=0, Sin_cycle=0, Singal_Cycle_step=0;
  169. void EnSinOut(int argc, char **argv)
  170. {
  171.     if(argc >= 2)
  172.     {
  173.         if(*argv[1] == '?')
  174.         {
  175.             sprintf(print_buf,"Vpp:%f, Offset:%f, Cycle:%f\r\n", Sin_vpp, Sin_offset, Sin_cycle);
  176.             rt_kprintf(print_buf);
  177.         }
  178.         else if(argc >= 4)
  179.         {
  180.             float val = user_atof(argv[1]);
  181.             if(val >=0 && val <=35)
  182.             {
  183.                 Sin_vpp = val;
  184.             }
  185.             else {
  186.                 sprintf(print_buf,"Error:Out range[0,35]! Vpp:%fV\r\n", Sin_vpp);
  187.                 rt_kprintf(print_buf);
  188.             }
  189.             val = user_atof(argv[2]);
  190.             if(val >=0 && val <=35)
  191.             {
  192.                 Sin_offset = val;
  193.             }
  194.             else {
  195.                 sprintf(print_buf,"Error:Out range[0,35]! Offset:%fV\r\n", Sin_offset);
  196.                 rt_kprintf(print_buf);
  197.             }
  198.             val = user_atof(argv[3]);
  199.             if(val >=1 && val <=100)
  200.             {
  201.                 Sin_cycle = val;
  202.                 Singal_Cycle_step = 2*3.1415926/100/Sin_cycle;
  203.             }
  204.             else {
  205.                 sprintf(print_buf,"Error:Out range[1,100]! Cycle:%fS\r\n", Sin_cycle);
  206.                 rt_kprintf(print_buf);
  207.             }
  208.             sprintf(print_buf,"Vpp:%fV, Offset:%fV, Cycle:%fS\r\n", Sin_vpp, Sin_offset, Sin_cycle);
  209.             rt_kprintf(print_buf);
  210.         }
  211.         else {
  212.             rt_kprintf("EnSinOut [vpp offset cycle]\r\n");
  213.         }
  214.     }
  215.     else {
  216.         rt_kprintf("EnSinOut [vpp offset cycle]\r\n");
  217.     }
  218. }
  219. MSH_CMD_EXPORT(EnSinOut, enable sinusoidal signal out);</p>
  220. <p style="line-height: 30px; text-indent: 2em;">void SetVoltage(int argc, char **argv)
  221. {
  222.     if(argc >= 2)
  223.     {
  224.         if(*argv[1] == '?')
  225.         {
  226.             sprintf(print_buf,"SetVoltageVal:%f\r\n", SetVoltageVal);
  227.             rt_kprintf(print_buf);
  228.         }
  229.         else {
  230.             float val = user_atof(argv[1]);
  231.             if(val >=0 && val <=35)
  232.             {
  233.                 Sin_vpp = 0;
  234.                 SetVoltageVal = val;
  235.                 sprintf(print_buf,"SetVoltageVal:%f\r\n", SetVoltageVal);
  236.                 rt_kprintf(print_buf);
  237.             }
  238.             else {
  239.                 sprintf(print_buf,"Error:Out range[0,35]! SetVoltageVal:%f\r\n", SetVoltageVal);
  240.                 rt_kprintf(print_buf);
  241.             }
  242.         }
  243.     }
  244.     else {
  245.         rt_kprintf("SetVoltage [val]\r\n");
  246.     }
  247. }
  248. MSH_CMD_EXPORT(SetVoltage, set voltage val);</p>
  249. <p style="line-height: 30px; text-indent: 2em;">void SetCurrent(int argc, char **argv)
  250. {
  251.     if(argc >= 2)
  252.     {
  253.         if(*argv[1] == '?')
  254.         {
  255.             sprintf(print_buf,"SetCurrentVal:%f\r\n", SetCurrentVal);
  256.             rt_kprintf(print_buf);
  257.         }
  258.         else {
  259.             float val = user_atof(argv[1]);
  260.             if(val >=0 && val <=5)
  261.             {
  262.                 SetCurrentVal = val;
  263.                 sprintf(print_buf,"SetCurrentVal:%f\r\n", SetCurrentVal);
  264.                 rt_kprintf(print_buf);
  265.             }
  266.             else {
  267.                 sprintf(print_buf,"Error:Out range[0,5]! SetCurrentVal:%f\r\n", SetCurrentVal);
  268.                 rt_kprintf(print_buf);
  269.             }
  270.         }
  271.     }
  272.     else {
  273.         rt_kprintf("SetCurrent [val]\r\n");
  274.     }
  275. }
  276. MSH_CMD_EXPORT(SetCurrent, set current val);</p>
  277. <p style="line-height: 30px; text-indent: 2em;">void VoltageCtrlEntry(void *parameter)
  278. {
  279.     uint8_t wait_i=0, replot_flag=0, overcurrent_flag=0;
  280.     float cpu_temp_ave=0, voltage_ave=0, current_ave=0;
  281.     dac_dev = rt_device_find("dac");
  282.     uint32_t ch = 1;
  283.     int32_t dac_val = 4095;
  284.     rt_device_control(dac_dev, RT_DAC_CMD_ENABLE, &ch);
  285.     rt_device_open(dac_dev, RT_DEVICE_OFLAG_RDWR);
  286.     rt_device_write(dac_dev, ch, &dac_val, 1);</p>
  287. <p style="line-height: 30px; text-indent: 2em;">    float Voltage_err=0, Voltage_err_old=0, Voltage_err_sum=0;</p>
  288. <p style="line-height: 30px; text-indent: 2em;">    float CtrlVoltage=0;</p>
  289. <p style="line-height: 30px; text-indent: 2em;">    float Singal_Cycle_i=0;</p>
  290. <p style="line-height: 30px; text-indent: 2em;">    rt_thread_sleep(100);
  291.     while(1)
  292.     {
  293.         if(adc1_dev != RT_NULL && adc1_dev->open_flag != RT_DEVICE_OFLAG_CLOSE)
  294.         {
  295.             rt_device_read(adc1_dev, 0, read_adc_buf, USER_ADC1_REGULAR_CH-1);
  296.             float cpu_temp = (CPU_V25 - read_adc_buf[0] * 3.3 / 4096) * 1000 / CPU_Avg_Slope + 25;
  297.             float voltage = read_adc_buf[1] * 3.3 / 4096 * 12;
  298.             float current = -(read_adc_buf[2] * 3.3 / 4096 - 1.65)/0.132;
  299.             if(current < 0)
  300.                 current = 0;</p>
  301. <p style="line-height: 30px; text-indent: 2em;">            if(Sin_vpp > 0.0001)        //使能正弦信号输出
  302.             {
  303.                 Singal_Cycle_i = fmod(Singal_Cycle_i + Singal_Cycle_step, 2*3.1415926);
  304.                 SetVoltageVal = Sin_offset + Sin_vpp / 2 * sin(Singal_Cycle_i);
  305.             }</p>
  306. <p style="line-height: 30px; text-indent: 2em;">            if(current <= SetCurrentVal)
  307.             {
  308.                 if(overcurrent_flag)
  309.                 {
  310.                     if(CtrlVoltage < SetVoltageVal-0.1)
  311.                     {
  312.                         CtrlVoltage += 0.1;
  313.                     }
  314.                     else if(CtrlVoltage > SetVoltageVal+0.1)
  315.                     {
  316.                         CtrlVoltage -= 0.1;
  317.                     }
  318.                     else {
  319.                         CtrlVoltage = SetVoltageVal;
  320.                         overcurrent_flag = 0;
  321.                     }
  322.                 }
  323.                 else {
  324.                     CtrlVoltage = SetVoltageVal;
  325.                 }
  326.             }
  327.             else {
  328.                 overcurrent_flag = 1;
  329.                 CtrlVoltage -= 0.1;
  330.             }</p>
  331. <p style="line-height: 30px; text-indent: 2em;">            Voltage_err = voltage - CtrlVoltage;
  332.             if(Voltage_err < 0.1 && Voltage_err > -0.1)
  333.             {
  334.                 Voltage_err_sum += Voltage_err;
  335.             }</p>
  336. <p style="line-height: 30px; text-indent: 2em;">            if(Voltage_err_old * Voltage_err < 0)
  337.             {
  338.                 Voltage_err_sum = 0;
  339.             }</p>
  340. <p style="line-height: 30px; text-indent: 2em;">            if(Voltage_err_sum > 2)
  341.             {
  342.                 Voltage_err_sum = 2;
  343.             }
  344.             else if(Voltage_err_sum < -2)
  345.             {
  346.                 Voltage_err_sum = -2;
  347.             }</p>
  348. <p style="line-height: 30px; text-indent: 2em;">            Voltage_err_old = Voltage_err;</p>
  349. <p style="line-height: 30px; text-indent: 2em;">            dac_val += VoltagePID_P * Voltage_err + VoltagePID_I * Voltage_err_sum;
  350.             if(dac_val < 0)
  351.             {
  352.                 dac_val = 0;
  353.             }
  354.             else if(dac_val > 4095)
  355.             {
  356.                 dac_val = 4095;
  357.             }
  358.             rt_device_write(dac_dev, ch, &dac_val, 1);</p>
  359. <p style="line-height: 30px; text-indent: 2em;">            cpu_temp_ave += cpu_temp;
  360.             voltage_ave += voltage;
  361.             current_ave += current;</p>
  362. <p style="line-height: 30px; text-indent: 2em;">            if(wait_i++ >= 100)
  363.             {
  364.                 if(rt_tick_get() - shell_getchr_tick > 5000)
  365.                 {
  366.                     if(replot_flag)
  367.                     {
  368.                         plot_ui();
  369.                         replot_flag = 0;
  370.                     }
  371.                     sprintf(print_buf,"%.2f ",voltage_ave / 101);
  372.                     rt_kprintf("\033[11;26H");
  373.                     rt_kprintf("\033[1;40;31m%s\033[0m",print_buf);</p>
  374. <p style="line-height: 30px; text-indent: 2em;">                    sprintf(print_buf,"%.3f ",current_ave / 101);
  375.                     rt_kprintf("\033[12;26H");
  376.                     rt_kprintf("\033[1;40;32m%s\033[0m",print_buf);</p>
  377. <p style="line-height: 30px; text-indent: 2em;">                    sprintf(print_buf,"%.3f ",voltage_ave / 101 * current_ave / 101);
  378.                     rt_kprintf("\033[13;26H");
  379.                     rt_kprintf("\033[1;40;34m%s\033[0m",print_buf);</p>
  380. </font><p style="line-height: 30px; text-indent: 2em;"><font face="宋体" size="4">                    sprintf(print_buf,"%.2f",cpu_temp_ave / 101);
  381.                     rt_kprintf("\033[14;26H");
  382.                     rt_kprintf(print_buf);
  383.                     rt_kprintf("\033[20;0H");
  384.                 }
  385.                 else {
  386.                     replot_flag = 1;
  387.                 }
  388.                 wait_i = 0;
  389.                 cpu_temp_ave = 0;
  390.                 voltage_ave = 0;
  391.                 current_ave = 0;
  392.             }
  393.         }
  394.         rt_thread_sleep(RT_TICK_PER_SECOND/100);
  395.     }
  396. }</font></p>
具体代码,有兴趣的朋友可以看一下,我就不一点点介绍分析了。测试用的,代码写的也比较随意,也没做注释。希望能对有需要的朋友提供帮助。也可以在此基础上再继续优化完善代码。后面等上传了演示视频再附上视频地址。祝大家工作学习开心愉快。
功能演示视频
源码地址


您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:国民技术股份有限公司
简介:国民技术股份有限公司(简称:国民技术)2000年源于国家“909”集成电路专项工程成立,2010年创业板上市(股票代码:300077),是通用MCU、安全芯片领先企业和国家高新技术企业。

153

主题

212

帖子

17

粉丝
快速回复 在线客服 返回列表 返回顶部