国民技术N32G031系列单片机的AD采样

用过一段时间国民技术N32G031系列单片机,编程模式几乎类同STM32系列(另外一个国产32位单片机品牌兆易创新好像也是仿STM32的编程模式,看来STM32的确是一款极其优秀的产品),但是价格实惠,功能也很齐全,软件支持包在官网都可以下载,所以总体感觉还是可以的,只是稳定性还有待验证。

最近在用N32G031做一款电源产品软件时发现了一个情况,这种情况以前几乎都是不关注的,就是AD转换的时间。我有10个通道要进行AD转换,每个通道采样64次,我的AD初始化函数和采样函数如下:

初始化函数:

/**
*@name: ADC_Initial
*@description: ADC initialization
*@params: none
*@return: none
*/
void ADC_Initial(void)
{ADC_InitType ADC_InitStructure;ADC_GPIO_Configuration();/* ADC configuration ------------------------------------------------------*/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(ADC, &ADC_InitStructure);//ADC1 regular channel13 configuration//ADC_ConfigRegularChannel(ADC, ADC_CH_13_PC2, 1, ADC_SAMP_TIME_55CYCLES5);//Enable ADC DMA//ADC_EnableDMA(ADC, ENABLE);//Enable ADCADC_Enable(ADC, ENABLE);// Check ADC Readywhile (ADC_GetFlagStatusNew(ADC, ADC_FLAG_RDY) == RESET);while (ADC_GetFlagStatusNew(ADC, ADC_FLAG_PD_RDY));channel = IS1;
}

获取每次AD转换值函数:这里设置每次采样的时钟周期是56

/**
*@name: ADC_GetData
*@description: get the converted AD value
*@params: ADC_Channel: channel to be converted
*@return: AD value we need
*/
uint16_t ADC_GetData(uint8_t ADC_Channel)
{uint16_t data;ADC_ConfigRegularChannel(ADC, ADC_Channel, 1, ADC_SAMP_TIME_56CYCLES5);// Start ADC Software ConversionADC_EnableSoftwareStartConv(ADC, ENABLE); //start software conversionwhile (ADC_GetFlagStatus(ADC, ADC_FLAG_ENDC) == 0) //wait for the conversion to be finished{}ADC_ClearFlag(ADC, ADC_FLAG_ENDC); //clear the end conversion flagADC_ClearFlag(ADC, ADC_FLAG_STR);data = ADC_GetDat(ADC);return (data);
}

应用程序采样所有通道AD值的函数(共10个通道,每个通道采样64次):

/**
*@name: ADC_Sample
*@description: sample all the channelsAccording to the ADC sampling clock(1 MHz) and the cycles(56 cycles) one sampling operation needs,here we need to sample 10 channels, and each channel has to be sampled 64 times. If the ADC samplingclock is 1 MHz, the total time it takes to sample all the 10 channels is 64*10*56/1M = 0.03584(S)about 36 milli-seconds.
*@params: none
*@return: none
*/
void ADC_Sample(void)
{uint16_t ad_data = 0;if (!sample_flag){if (sample_cnt == 0) //the first value should be discarded when switching channel{sample_cnt = 1;return;}switch (channel){case IS1:{ad_data = ADC_GetData(IS1);is1_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is1_sample_value_filt = is1_sample_sum >> 6; //get the average AD valueis1_sample_sum = 0;channel = IS2;              //next channel}break;}case IS2:{ad_data = ADC_GetData(IS2);is2_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is2_sample_value_filt = is2_sample_sum >> 6;is2_sample_sum = 0;channel = IS3;}break;}case IS3:{ad_data = ADC_GetData(IS3);is3_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is3_sample_value_filt = is3_sample_sum >> 6;is3_sample_sum = 0;channel = IS4;}break;}case IS4:{ad_data = ADC_GetData(IS4);is4_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is4_sample_value_filt = is4_sample_sum >> 6;is4_sample_sum = 0;channel = IS5;}break;}case IS5:{ad_data = ADC_GetData(IS5);is5_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is5_sample_value_filt = is5_sample_sum >> 6;is5_sample_sum = 0;channel = IS6;}break;}case IS6:{ad_data = ADC_GetData(IS6);is6_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is6_sample_value_filt = is6_sample_sum >> 6;is6_sample_sum = 0;channel = IS7;}break;}case IS7:{ad_data = ADC_GetData(IS7);is7_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is7_sample_value_filt = is7_sample_sum >> 6;is7_sample_sum = 0;channel = IS8;}break;}case IS8:{ad_data = ADC_GetData(IS8);is8_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;is8_sample_value_filt = is8_sample_sum >> 6;is8_sample_sum = 0;channel = TEMP;}break;}case TEMP:{ad_data = ADC_GetData(TEMP);otp_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;otp_ad_value = otp_sample_sum >> 6;otp_sample_sum = 0;channel = VOS;}break;}case VOS:{ad_data = ADC_GetData(VOS);vos_sample_sum += ad_data;if (++sample_cnt > SAMPLE_COUNT){sample_cnt = 0;vdc_ad_value = vos_sample_sum >> 6;vos_sample_sum = 0;sample_flag = 1;//sampleing finished flagchannel = IS1;}break;}default:{channel = IS1;sample_cnt = 0;is1_sample_sum = 0;is2_sample_sum = 0;is3_sample_sum = 0;is4_sample_sum = 0;is5_sample_sum = 0;is6_sample_sum = 0;is7_sample_sum = 0;is8_sample_sum = 0;vos_sample_sum = 0;otp_sample_sum = 0;break;}}}
}

系统时钟及ADC模块的时钟配置函数(其中配置AD模块的1 MHz时钟是必须的):

/*** @brief  Configures the different system clocks.*/
static void RCC_Configuration(void)
{// PCLK1 = HCLK/4, configure the clock of APB1 and times uses the prescaled APB1RCC_ConfigPclk1(RCC_HCLK_DIV4);// Enable peripheral clocks ------------------------------------------------// Enable TIM1 clocks//RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_TIM1, ENABLE);//Enable DMA clocks//RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);//Enable GPIO clocksRCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOF, ENABLE);//Enable ADC clocksRCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ADC, ENABLE);//RCC_ADCHCLK_DIV16ADC_ConfigClk(ADC_CTRL3_CKMOD_AHB, RCC_ADCHCLK_DIV16);//enable ADC1M clock//RCC_EnableHsi(ENABLE);RCC_ConfigAdc1mClk(RCC_ADC1MCLK_SRC_HSE, RCC_ADC1MCLK_DIV8);//enable USART1 clockRCC_EnableAPB2PeriphClk(USARTy_GPIO_CLK, ENABLE);RCC_EnableAPB2PeriphClk(USARTy_CLK, ENABLE);//TIM3 and TIM clock enableRCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM3, ENABLE);//RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM6, ENABLE);
}

AD检测滤波函数(每20毫秒设置相关警告位,主要是输入电压是否欠压或者过压):

/**
*@name: ADC_Status_Check
*@description: ADC filter, the input voltage and OTP check
*@params: none
*@return: none
*/
static void ADC_Status_Check(void)
{if (vdc_ad_value <= VDC_UV_OFF_AD) //DC under and over voltage filtervdc_uvp_off++;if (vdc_ad_value >= VDC_UV_ON_AD)vdc_uvp_recovered++;if (vdc_ad_value >= VDC_OV_OFF_AD)vdc_ovp_off++;if (vdc_ad_value <= VDC_OV_ON_AD)vdc_ovp_recovered++;if (otp_ad_value >= OTP_OFF_AD) //OTPotp_off++;if (otp_ad_value <= OTP_RECOVER_AD)otp_recovered++;if (adc_filter_cnt >= IO_FILTER_CNT){if (vdc_uvp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_UVP == 0) //VDCAlarmStatus.alarm.VDC_UVP = 1;if (vdc_uvp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_UVP == 1)AlarmStatus.alarm.VDC_UVP = 0;if (vdc_ovp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_OVP == 0)AlarmStatus.alarm.VDC_OVP = 1;if (vdc_ovp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_OVP == 1)AlarmStatus.alarm.VDC_OVP = 0;if (otp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.OTP == 0) //OTPAlarmStatus.alarm.OTP = 1;if (otp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.OTP == 1)AlarmStatus.alarm.OTP = 0;if (AlarmStatus.alarm.VDC_OVP == 0 && AlarmStatus.alarm.VDC_UVP == 0){AlarmStatus.alarm.VIN_STATUS_ERR = 0;}else{AlarmStatus.alarm.VIN_STATUS_ERR = 1;}adc_filter_cnt = 0;vdc_uvp_off = 0;vdc_uvp_recovered = 0;vdc_ovp_off = 0;vdc_ovp_recovered = 0;otp_off = 0;otp_recovered = 0;}else{adc_filter_cnt++;}
}

根据手册说明及咨询芯片厂的相关技术人员,ADC模块的采样时钟频率固定是1MHz,每次采样的时间软件设置为56个时钟周期(这个可以修改),另外每次AD转换还额外需要12.5个时钟周期,所以完成10个通道采样(每个通道采样64次)所需要的总时间是:(56+12.5)*10*64/1000000 = 0.04384秒,差不多就是44毫秒。

我的程序在做AD检测滤波(非AD转换,对AD转换完成后的平均值再次进行一段时间的滤波处理,避免电源输出产生误动作)时只有20毫秒,并且初始时所有警告标志位都是清零的,尤其是输入电压欠压和过压标志,所以在电源开机时会有一个20毫秒输出正常打开,然后又有一个40毫秒左右的关闭,最后又是完全正常的现象。结合这个现象和AD转换10个通道所需的总时间,那20毫秒就是AD滤波的时间,在此期间由于所有标志位都清零,电源输出正常打开,20毫秒滤波过后,由于此时AD转换还未完成(需要40毫秒左右),所以输入电压欠压警告位被置位(AD检测未完成,AD平均值为0),结果输出关闭,只有等到一轮AD检测完全完成且AD滤波也检测到完整的AD值后警告标志位才会清除,之后输出就一直正常了。

如果要解决这个问题,可以把AD滤波的时间加长,至少要大于AD转换一轮所需要的时间,或者如果对AD转换精度要求不高的话,可以把采样次数减少或者把每次采样所需要的时钟数设置得小一些。最为直接的做法就是初始将所有警告标志位置1。这样开机时是异常的,只有AD检测结果正常后电源才打开输出。

在这里我也只是记录下出现问题后要根据现象找出原因,积极思考,再找解决办法就是比较容易些的事情了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/89860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flink容错机制

容错机制 在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 检查点的保存 1&#xff09;周期性的触发保存 “随时存档”确实恢复起来方便&#xff0c;可是需要我们不停地做存档操作。如果每处理一条数据就进行检查点的保存…

React(react18)中组件通信06——redux-toolkit + react-redux

React&#xff08;react18&#xff09;中组件通信06——redux-toolkit react-redux 1 前言1.1 redux 和 react-redux1.2 关于redux-toolkit1.2.1 官网1.2.2 为什么要用Redux Toolkit&#xff1f; 1.3 安装 Redux Toolkit1.4 Redux Toolkit相关API 2. 开始例子——官网例子2.1 …

巨人互动|Facebook海外户Facebook内容的类型

随着人们日益依赖的社交媒体来进行信息获取与交流&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;那么Facebook的内容都有哪些类型呢&#xff1f;下面小编来讲讲吧&#xff01; 1、实时发生的事 我们需要实时了解时事动态&#xff0c;这样可以使用户对品牌发…

纯css html 真实水滴效果

惯例,不多说直接上图 秉承着开源精神,我们将这段代码无私地分享给大家&#xff0c;因为我们深信&#xff0c;信息的共享和互相学习是推动科技进步的关键。我们鼓励大家在使用这段代码的同时&#xff0c;也能够将其中的原理、思想和经验分享给更多的人。 这份代码是我们团队用心…

VR庆中秋丨奇幻月景邀您共赏!

中秋佳节&#xff0c; 如何来一场别开生面的云游月景体验&#xff1f; 3DVR技术开启中秋过节新姿势&#xff0c; 嫦娥奔月伴玉兔、 太白花间饮美酒、 吴刚月下伐桂树…… 立体化还原璀璨的传统中秋文化&#xff0c; 还有趣味猜灯谜活动&#xff0c; 丰富豪礼等你来拿&a…

如何使用ChatGPT构建一个Web应用程序?

围绕ChatGPT的最大卖点之一是它可以成为一种有效的编程工具。其想法是这样的&#xff1a;你用自然语言描述需求&#xff0c;该聊天机器人生成满足该需求的代码。但是ChatGPT在这方面到底有多好呢&#xff1f; 还有什么比亲自测试一下更好的方法呢&#xff1f;我们让ChatGPT从头…

Spring整合MyBatis原理

Spring整合MyBatis原理 整合包中&#xff08;上篇文章中的配置文件&#xff0c;文章传送门&#xff1a;Spring整合第三方框架-MyBatis整合Spring实现-CSDN博客&#xff09;提供了SqlSessionFactoryBean和一个扫描Mapper的配置对象&#xff0c;SqlSessionFactoryBean一旦被实例…

FPGA千兆网 UDP 网络视频传输,基于88E1518 PHY实现,提供工程和QT上位机源码加技术支持

目录 1、前言版本更新说明免责声明 2、我这里已有的以太网方案3、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条UDP协议栈UDP视频数据组包UDP协议栈数据发送UDP协议栈数据缓冲IP地址、端口号的修改Tri Mode Ethernet MAC介绍以及移植注意事项88E1518 PHYQT上位机和源码…

【软件测试】开发/测试模型

开发/测试模型 瀑布模型 设计&#xff1a;技术文档(设计那些接口&#xff0c;库表&#xff0c;mq&#xff0c;定时任务)&#xff0c;UI视觉稿 特点&#xff1a;线性的结构。 优点&#xff1a;每个阶段做什么&#xff0c;产出什么非常清晰 缺点&#xff1a;测试人员介入太晚…

解释器风格架构C# 代码

/*解释器风格架构是一种基于组件的设计架构&#xff0c;它将应用程序分解为一系列组件&#xff0c;每个组件负责处理特定的任务。这种架构有助于提高代码的可维护性和可扩展性。以下是如何使用C#实现解释器风格架构的步骤&#xff1a;定义组件&#xff1a;首先&#xff0c;定义…

Makefile快速上手

Makefile学习 https://maxwell-lx.vip/basic-usage-make/ https://zhuanlan.zhihu.com/p/92010728 https://zhuanlan.zhihu.com/p/350297509 一、是什么 可以理解为一个自动化的编译脚本&#xff0c;避免繁琐的手动编译过程。有点类似shell脚本。 1.1 从小例子入手 &…

SQLAlchemy常用数据类型

目录 SQLAlchemy常用数据类型 代码演示 代码分析 SQLAlchemy常用数据类型 SQLAlchemy 是一个Python的SQL工具库和对象关系映射(ORM)工具&#xff0c;它提供了一种在Python中操作数据库的高效方式。下面是SQLAlchemy中常用的一些数据类型&#xff1a; Integer&#xff1a;整形&…

静态路由+BFD实例

项目拓扑与项目需求 项目需求 ① 主链路为电信&#xff0c;电信链路出故障时&#xff0c;业务数据流量切换到联通链路 实验步骤 步骤1&#xff1a;设备重命名以及IP地址的配置 设备 接口编号 IP地址 AR1 G0/0/0 10.0.13.1/24 G0/0/1 10.0.14.1/24 AR2 G0/0/0 10.0…

中国JP-10燃料行业市场研究与预测报告(2023版)

内容简介&#xff1a; 高密度燃料是指以石油基、煤基和生物质基烃类为原料&#xff0c;通过聚合、加氢、异构等工艺合成的密度大于0.85 gcm-3的饱和多环碳氢化合物&#xff0c;广泛应用于航空航天领域。由于高密度燃料密度大和体积热值高等特点&#xff0c;飞行器在油箱体积一…

LuatOS-SOC接口文档(air780E)-- fota - 底层固件升级

fota.init(storge_location, len, param1)# 初始化fota流程 参数 传入值类型 解释 int/string fota数据存储的起始位置 如果是int&#xff0c;则是由芯片平台具体判断 如果是string&#xff0c;则存储在文件系统中 如果为nil&#xff0c;则由底层决定存储位置 int 数据存…

Windows 下安装及配置 MySQL 8.1 (图文教程)

目录 下载 MySQL安装 MySQL配置 MySQL修改密码配置环境变量 卸载 MySQL开源项目微服务商城项目前后端分离项目 下载 MySQL 访问 MySQL 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 下载 MySQL 时&#xff0c;你可以选择 ZIP 包或 MSI 安装&#xff1a; ZIP包…

十六,镜面IBL--预滤波环境贴图

又到了开心的公式时刻了。 先看看渲染方程 现在关注第二部分&#xff0c;镜面反射。 其中 这里很棘手&#xff0c;与输入wi和输出w0都有关系&#xff0c;所以&#xff0c;再近似 其中第一部分&#xff0c;就是预滤波环境贴图&#xff0c;形式上与前面的辐照度图很相似&#…

安达发|印刷包装行业利用APS自动排产系统迎来绿色革命

随着环保意识的不断提高&#xff0c;印刷包装行业也在寻求绿色发展之路。近年来&#xff0c;该行业在材料研发、生产工艺等方面取得了显著成果&#xff0c;为实现可持续发展奠定了基础。 印刷包装行业作为现代工业的重要组成部分&#xff0c;其发展水平直接影响到国民经济的整体…

SpringBoot 学习(二)配置

2. SpringBoot 配置 2.1 配置文件类型 配置文件用于修改 SpringBoot 的默认配置。 2.1.1 properties 文件 **properties ** 是属性文件后缀。 文件名&#xff1a;application.properties 只能保存键值对。 基础语法&#xff1a;keyvalue namewhy注入配置类 Component //…

为什么u盘在mac上显示不出来

插入U盘是个看似简单的操作&#xff0c;但有时候在Mac电脑上却出现了无法显示U盘的情况。这样的问题是非常让人头疼的&#xff0c;特别是当你急需使用U盘中的文件时。那么&#xff0c;究竟为什么U盘在Mac上会显示不出来呢&#xff1f;今天就让我们一起来深入了解一下这个问题&a…