Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
DMA
国民技术_N32G45x
RT-Thread一般讨论
[N32G457] 基于RT-Thread和N32G457的简易便捷式可调电压源
发布于 2022-03-02 20:57:35 浏览:1493
订阅该版
本设计是用来参加《创新“芯”引擎 | 国民技术N32G457 RT-Thread设计大赛》的作品。初衷是由于做军品要求国产化,所以最近也一直在测试国内不同厂家的32芯片的性能以及开发便捷性和最重要的稳定性等问题。之前也一直在用RTT做开发。浏览官网的时候发现有这个比赛,就顺便参加一下。至于做什么,肯定不能做我工作上的东西,都是军品保密的。只能想一些简单的。正好想到平常测试,特别是外出测试,经常因为电源问题而苦恼。于是想做一款简易的可调电压源,这样只需要带一个充电宝,就可以输出1~35V的电压,峰值电流基本能达到5A以上。也就可以驱动大多数的设备了。当然,功耗大的设备需要大充电宝才能工作时间长一些,但至少解决了供电电压不匹配的问题,可以应急使用了。 由于此比赛要求用官方给的核心板做开发,所以计划先简单实现一下主体功能。后面再具体做板实现整体功能。这次的测试硬件如下图,N32的最小系统板加一块我这边做其它测试的板子。这块板子只用了LTC3780部分的电路。 ![国民技术核心板2.jpg](https://oss-club.rt-thread.org/uploads/20220302/ab7014df5d5dbba136e2c2430ea8054c.jpg.webp) 具体电路如下图: ![image.png](https://oss-club.rt-thread.org/uploads/20220302/243916a254c41c5ad5a4969be8423520.png.webp) 其中TEC-对地就是最终的电源输出接口,TEC_DAC为输出电压调整端口,接N32板的PA4引脚。TEC_i为输出电流检测端口,接N32板的PC1引脚。电源输出对地飞了个110K和10K的分压电阻,分压短点接N32板的PC0引脚,用于测量输出电压值(暂时没有对测量电压进行标定)。 测试版没有做显示界面(不太想飞线接屏了🤣)。取而代之的是shell界面。设置电压和电流值也用的shell命令。后面做电路的时候再改成显示屏和按键以及编码器旋钮的交互。测试界面如下: ![image.png](https://oss-club.rt-thread.org/uploads/20220302/7f562f93527b04e957873ddd66e870ae.png) 具体实现了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
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
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); } } ``` 具体代码,有兴趣的朋友可以看一下,我就不一点点介绍分析了。测试用的,代码写的也比较随意,也没做注释。希望能对有需要的朋友提供帮助。也可以在此基础上再继续优化完善代码。后面等上传了演示视频再附上视频地址。祝大家工作学习开心愉快。 [功能演示视频](https://www.bilibili.com/video/BV1Wa411h7Di?p=1&share_medium=android&share_plat=android&share_session_id=0292055d-3fee-4063-a2c2-f72f36c7ac64&share_source=WEIXIN&share_tag=s_i×tamp=1646356356&unique_k=KAH3D2I) [源码地址](https://gitee.com/chengjili/n32g457_rtthread.git)
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
吉利咕噜2022
国防科大-军品研发
文章
18
回答
3
被采纳
2
关注TA
发私信
相关文章
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
15
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部