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

Posted RT-Thread物联网操作系统

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[N32G457] 基于RT-Thread和N32G457的简易便捷式可调电压源相关的知识,希望对你有一定的参考价值。

本文是RT-Thread用户@chengjili 原创发布,是用于参加RT-Thread与国民技术联手推出N32G457 RT-Thread设计大赛,原文:https://club.rt-thread.org/ask/article/3423.html

本设计是用来参加《创新“芯”引擎 | 国民技术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周期.png](https://oss-club.rt-thread.org/uploads/20220302/917d37e6179a89c16a785a9905cb30ba.png.webp)
我用的ADC的规则组加入了4个通道,第一通道是CH18,既Vrefint。第二通道是CH16,测了一下CPU温度,最终是要测量整体电路板的温度,根据电路板温度驱动散热风扇调控降温的。最终是用CPU温度调控还是在发热器件旁布一个热敏电阻后续再定。这里由于最小系统板和电源板分开的,CPU温度也测不到发热器件的温度。所以也只是简单的采集显示了一下。第三通道是PC0引脚,用于采集电压输出的反馈。第四通道是PC1引脚,用于采集电流反馈。测试板上面没有做采样电阻,而是用的一个霍尔感应器,量程是±10A的。所以精度有点差。最终也没必要用这个芯片,计划最终做板还是用采样电阻的方案,N32又自带运放,直接可以放大输入给ADC采集。
具体的ADC数据处理,是先根据电源纹波在短暂时间内差异不大的理论下,对每一次循环采集的4通道数据,根据内部基准电压做了个补偿。这样后面根据ADC数据计算的电压才是准确的。我这里实测的数据如下图:
![image.png](https://oss-club.rt-thread.org/uploads/20220302/a6a82d0adeea3bcb8ed9674884839b8a.png)
数组的0,4,8,12...就是保存的实时采集的Vrefint的ADC数据,而此时的VrefP的供电电压是3.265V。可以计算得到Vrefint的实际电压是1.2068。如下图是官方给的Vrefint的电压范围。
![image.png](https://oss-club.rt-thread.org/uploads/20220302/57bd77f95f90ec7a1a843f008188376d.png.webp)
根据实际的Vrefint可以推算出VrefP供电电压为标准的3.3V供电时,Vrefint的ADC转换值为1497.89。我看默认配置已经开启了FPU,所以这里直接用浮点数了,可以提高一点精度。
![FPU.png](https://oss-club.rt-thread.org/uploads/20220302/81fc4f54d36cb9b31e11490e2b47b38d.png.webp)
如下是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);

具体代码,有兴趣的朋友可以看一下,我就不一点点介绍分析了。测试用的,代码写的也比较随意,也没做注释。希望能对有需要的朋友提供帮助。也可以在此基础上再继续优化完善代码。后面等上传了演示视频再附上视频地址。祝大家工作学习开心愉快。

以上是关于[N32G457] 基于RT-Thread和N32G457的简易便捷式可调电压源的主要内容,如果未能解决你的问题,请参考以下文章

N32G457基于RT-Thread和N32G457的墨水屏日历

N32G457基于RT-Thread和N32G457的涂鸦云远程监控

N32G457 基于RT-Thread和N32G457的智能工业网关

N32G457基于RT-Thread和N32G457的家用环境仪表盘

N32G457 基于RT-Thread和N32G457的传送带物品计数器

N32G457 基于RT-Thread和N32G457 数码管时钟