STM32之ADC的学习心得(std+HAL)
STM32的ADC配置问题
问题介绍
最近要使用STM32F103C8T6来做个数字万用表,于是开始学习STM32,要用到32内部的12位ADC
等于是刚刚接触STM32,一切从零开始,现在分享下如何简单的使用ADC
预备知识
RCC:
这个是用来设置时钟的,比如我可以设置我的系统时钟频率等
TIM:
顾名思义,是timer的缩写,是定时计数器.
RCC 和 TIM的区别:
RCC用来设置我32的系统时钟频率或者是一些其他硬件的时钟频率
而TIM是在某个时钟频率下工作的一个计数器,这个频率可以来自RCC的设置,也可以来自外部
注意,RCC设置频率的来源也可以是外部或者内部(内部不准确,我们一般不用,这也是为什么要外接8MHz晶振的原因),而后产生一个内部时钟频率送给TIM
为什么要说时钟呢?因为我要使ADC的采样率达到最大,也就是1MHz的采样率,而达到这样的采样率就需要设置ADC的时钟频率,ADC最大时钟频率是14MHz。这两者什么关系呢?
后面介绍,总之先知道要达到最大的采样率就需要设置我们的时钟
以下正文
那么就让我开始来配置RCC吧!
No.1
首先我们应该用外部接的8MHz晶振来做时钟源。外部高速晶振:HSE;内部高速晶振:HSI
用 void RCC_HSEConfig(u32 RCC_HSE) 这个函数来启动,内部参数设置 RCC_HSE_ON
RCC_HSEConfig(RCC_HSE_ON);//开启8MHz外部晶振
然后检测外部高速晶振是否正常启动
用 RCC_WaitForHSEStartUp(); 这个函数, 这个函数返回 SUCCESS 这个参数则代表正常启动
ErrorStatus HSEStartUpStatus;//设置标志位
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)//若外部晶振正常启动
{
/* Add here PLL ans system clock config */
}
这时候我们要通过PLL锁相环来使 外部接的晶振作为输入,输出另一个稳定频率的时钟信号
即我们要用PLL来进行倍频
这里我们设置PLL输出 = 8MHz * 7 = 56MHz (那就是要进行7倍频)
//RCC_PLLSource_HSE_Div1 意思是 PLL的输入时钟 = HSE时钟频率
//RCC_PLLMul_7 表示 7倍频
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);
然后输出
RCC_PLLCmd(ENABLE);//PLL输出使能
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL输出
到目前为止我们得到一个56MHz的时钟频率
No.2
然后我们要利用PLL输出的这个频率作为我们STM32的系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟为56MHz
while(0x08 != RCC_GetSYSCLKSource());//等待系统时钟被正确设置
这样系统时钟就被我们设置好了
接下来的任务就是要设置AHB时钟了
what?AHB时钟又是个啥?
AHB(Advanced High performance Bus)高级高性能总线 或者我们叫它 系统总线
就是我们要设置STM32内部总线的时钟频率
而且AHB又有高低速之分,也就是说我们要设置两个时钟分别给高速AHB和低速AHB
AHB掌管着DMA时钟,SRAM时钟和FLITF时钟,它并不直接管ADC的时钟,那我们为什么还要设置它呢?先别急,往下看
RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟(HCLK)
RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟 PLCK2为56MHz (最大72MHz)
RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟 PLCK1为28MHz (最大36MHz)
//注:这里面Div1表示一倍分频,也就是不分频。 PLCK2 = HCLK = 56MHz
//Div2表示2倍分频 PLCK1 = HCLK / 2 = 28MHz
总线设置好了我们就终于可以开始设置ADC的时钟频率了
但这里又要提到一个名词APB
APB(Advanced Peripheral Bus)外围总线
这个才是直接管ADC时钟的总线,APB又分APB1 和 APB2
APB1管TIMx (x = 2, 3, 4 ……) WWDG,SPI2, USART2, USAT3, I2C, CAN的时钟
APB2管TIM1, GPIOx, ADC1, ADC2, SPI1, USART1的时钟
很明显我们只需要对APB2的ADC功能进行设置就行
//使能ADC & GPIOA
//这里我用ADC1采样,PA0端口,具体看各个芯片的数据手册
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
重点来了
现在来设置ADC了!!!
来上函数 void ADC_ADCCLKConfig(u32 RCC_ADCCLKSource)
看看这个参数 RCC_ADCCLKSource: 定义ADCCLK,该时钟源自APB2时钟(PCLK2)
懂了吧,为啥非要对AHB进行设置,ADC的时钟来源于AHB的PLCK2
//RCC_PCLK2_Div4 意思是 ADC时钟 = PCLK2 / 4 = 14MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div4);//ADC最大时钟频率是14MHz
至此,我们就对ADC时钟设置完了,一般来说,我们的STM32系统时钟都是设置的是72MHz,但这里我们为什么非要费那么老劲来设置RCC呢?还是上面这个函数,它的参数一共就4个
Div2 Div4 Div6 Div8 也就是2 4 6 8 分频。
72MHz并不能通过这四个分频得到14MHz最大时钟,所以我们特地设置56MHz,通过4分频,产生14MHz的ADC最大时钟频率。72MHz最大能做到 72 / 6 = 12MHz
来吧,上一份完整的RCC代码
static void RCC_ConfigInitail()
{ErrorStatus HSEStartUpStatus;FlagStatus Status;//RCC配置RCC_DeInit();//重置RCC_HSEConfig(RCC_HSE_ON);//外部8MHz晶振启动!HSEStartUpStatus = RCC_WaitForHSEStartUp();if(SUCCESS == HSEStartUpStatus)//若启动成功{RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);//56MHz PLL输出RCC_PLLCmd(ENABLE);//PLL输出使能while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL输出成功 //设置系统时钟56MHzRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);while(0x08 != RCC_GetSYSCLKSource());//等待设置成功 RCC_HCLKConfig(RCC_SYSCLK_Div1);RCC_PCLK2Config(RCC_HCLK_Div1);//PLCK2 56MHzRCC_PCLK1Config(RCC_HCLK_Div2);//PLCK1 28MHz //使能APB2外设时钟 ADC & GPIOARCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div4);//ADC1时钟频率 14MHz}
}
等有时间再更新后面的
2018 / 4 / 4 更新……
我是分割线
好了,我们现在来说一说ADC的配置吧
ADC的配置就没什么值得说的,它没有定时器难理解
直接上代码,注释就是解释
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;//GPIO配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//GPIO采用模拟输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//对PA0初始化
//因为我使用的是ADC1_IN0就是通道0,而这对应STM32C8T6的PA0口
//对于其它型号的要具体看芯片手册//ADC配置
ADC_DeInit(ADC1);//重置ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC1和ADC2单独工作,互不影响
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//ADC单次采样,即采样一次就停止
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //ADC单通道采样(ENABLE是多通道扫描)
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//软件触发ADC
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//ADC通道转换数目,我们只用一个ADC,那么就是1
//对于多通道采集才使这个值 >= 2, 取值范围是1~16ADC_Init(ADC1,&ADC_InitStructure);//初始化
ADC_Cmd(ADC1, ENABLE);//使能//ADC校准
ADC_ResetCalibration(ADC1);//重置ADC校准器
while(ADC_GetResetCalibrationStatus(ADC1));//等待重置结束ADC_StartCalibration(ADC1);//开始校准
while(ADC_GetCalibrationStatus(ADC1));//等待校准完成ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_1Cycles5);
最后一句话是重点
这句话是重点,看函数名就知道是对ADC通道配置的
这里我们就用一个通道,所以就只写一句,如果有多通道,那么就多写几句,参数变一下即可
要知道ADC转换周期是12.5个ADC时钟周期 而ADC_SampleTime_1Cycles5就是ADC采样时间为
1.5个周期,所以一共12.5 + 1.5 = 14个周期
14MHz / 14 = 1MHz,这样我们就能得到1MHz最大的采样率了
除了1.5以为,STM固件库还给出了其它的一些稀奇古怪的周期
例如:
7.5
13.5
28.5
41.5
55.5
71.5
239.5
呐,就这,ADC最基本的应用就配置完成了
剩下的就是些不痛不痒的代码了
static u16 Get_ADC_Value()
{ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件启动,ADC开始转换while(ADC_GetSoftwareStartConvStatus(ADC1));//等待转换完成return ADC_GetConversionValue(ADC1);//返回得到的ADC值
}
到目前为止ADC的最简单的配置就完成了
下面是我个人的情况,可以选择性阅读
下面是我实力分析为什么在高采样率的情况下,上面这些普通的代码不适合测AC交流
这么说吧,这是我一开始写的代码,目的是为了采集交流信号(这不废话么)
要求是测得10HZ ~ 100KHZ的交流信号的有效值
于是我想采用计算真.有效值的方法进行计算
真.有效值的计算公式是
sqrt( 1/T * ∫ f(x)² dt ) (积分区间是0 ~ T)
将之变成离散的信号处理,那么公式就又积分变到求和
sqrt( 1/n * ∑ f(x)² )(求和的话,那就是从0 ~ n)
那么也就是说我需要采样,一个周期采样n个点,n越多,计算得到的真有效值就越接近真值,跟做个示波器差不多了
如此一来我写了如下的程序
//times表示一个周期采样的个数,即上面说的n
float Get_ADC_EffectiveValue(u8 times)
{float sum = 0.0;//∑ f(x)² float temp = 0.0;//得到返回的ADC值u8 t = 0;for(t = 0; t < times; t++){//12位采样(0xFFF = 4096),STM32是3.3V供电//所以 3.3 / 0xFFF ≈ 0.000805664temp = (Get_ADC_Value() - 2048) * 0.000805664f;sum += temp * temp;}return (float)sqrt(sum / times);
}
这里我是通过电路将AC信号控制在0 ~ 3.3V,也就是原本的AC信号加上一个DC直流偏置
直流偏置 = VCC / 2,所以我要Get_ADC_Value()之后减去直流偏置,得到原来真实的量
这里再补充一个小细节,再0.000805664后加上个f,意思是告诉CPU这是一个float型常量,别给我弄成个double型了,因为STM32是32位CPU,那么也就是说在32位以下的数据计算速度都差不多。
float占4个字节(32位),double占8个字节(64位),很明显处理float要比double快多了,以后建议能用float就尽量用float
那么好了,我的具体方案是这样的,通过TIM的输入捕获功能来捕捉到信号的上升沿,在通过计算差值得到周期T,我一共捕捉11个上升沿,这样我就得到了10个周期,求平均值后使误差减小
然后ADC开始采样,根据得到的周期来确定一个周期我要采样几个点,就这样
然而后面我发现一系列问题,导致我现在否定了这个方案。
来,我们来分析分析
while(1)
{反复分析;//2333333
}
首先我们的定时计数器是工作在56MHz下的,我设置的可计数值为65535,不分频
那么有可能这个周期比较长,我计数器在从0计数到65535后,还没捕捉到第二个上升沿,那么我的这个计数器就会溢出。不要紧,我设置一个全局变量u8 OverFlow = 0;//记录溢出次数
每当我的计数器溢出的话,OverFlow ++;
我计数的Counter = f_TIM / f_IN
f_TIM表示计数器频率即56MHz,f_IN表示输入信号的频率
假如输入信号频率f_IN = 100KHz,那么Counter = 560,嗯,没什么问题
假如f_IN = 10Hz,那么Counter = 5.6 * 1e6,这个可以么?不会太大么?
OverFlow 最大计数255,所以最大Counter = 255 * 65536,这么看有点看不出来,不是很直观
反过来求一下5.6 * 1e6 / 65536 后向下取整为85,也就是说OverFlow 最大只会计数到85,也不是问题
也有人会问,中断不管么?有时间误差啊!
管什么?最多85 + 1次中断,+1是因为捕获到上升沿。急什么,忽略这个时间误差
假如我只知道f_IN,我想知道我的f_TIM应该设置多少?
这样,我们假设Counter >= 100, 那么我计数器从0计数到1的时间间隔t = T_IN / Counter
也就是最小误差(仪器误差)t <= T_IN /100,嗯,1%不到的误差,可以忽略了。
结论一:即当Counter >= 100 时,可以忽略计算周期的误差
那么我就取Counter >= 100
所以f_TIM >= 100 * f_IN,STM32最大72M时钟频率,所以理论上能精确计算的最大输入信号周期的为720KHz。当然,这不是绝对的,你可以通过输入捕获预分频,来扩大这个值,误差还是 1 / Counter这里我们不做过多讨论
由于我们的Counter最小为560,所以根据结论一 可知,一个周期下,输入捕获计算周期的误差忽略,再加上我还通过取平均来减小误差,使得计算得到的误差更小了
结论二:输入捕获计算周期的误差可以忽略不计,不需要管输入捕获了
接下来我们来分析ADC
(嘿嘿嘿!好玩的来了!)
我们得到了输入信号的周期T后,就可以计算一个周期下,ADC采样的次数了
设一个周期内,ADC采样次数为n
则有n = f_ADC / f_IN
f_ADC为ADC的采样频率,f_IN为信号输入频率
那么我们还采用刚才的思路看看,已知f_IN,取n >= 100
则 f_ADC >= 100 * f_IN
确实,前面说过,n越大,越接近积分得到的结果
但是!!!CPU计算也需要时间啊
看看这段代码
for(t = 0; t < times; t++)// 2n{temp = (Get_ADC_Value() - 2048) * 0.000805664f;// 3nsum += temp * temp;// 3n}
这时候我们就不得不计算程序的时间复杂度了
我就假设+ - * / 都是一样的计算时间,都是一个执行周期(真正的情况下乘除要比加减慢,更不要说你根本不知道编译器翻译成汇编之后会有多少条指令,我这么算算是少的)
t < times 需要n次判断
t++需要n次计算
我就假设Get_ADC_Value()得到值后存入内存不要时间
Get_ADC_Value() - 2048需要n次计算
- 0.000805664f需要n次计算
赋值到temp需要n次计算
temp * temp需要n次计算
sum + (temp * temp)需要n次计算
赋值到sum需要n次计算
呵呵,一共是8n的时间复杂度
ARM3官方给出的平均计算速度是1.25MIPS/MHz
意思是1MHz频率下,每秒执行1.25M条指令。我们是56MHz主频,那么就是每秒56M * 1.25条指令
执行一条指令需要 1 / (56 * 1.25 * 1e6)
也就是说光是用在计算上的时间就需要
t = 8n / (56 * 1.25 * 1e6) ≈ 0.114n (单位us)
我们来看看n取100时候的情况
t = 11.4us, 而对于100KHz信号来说,100KHz = 10us,呵呵,光计算的时间就差了一个周期!!
对于10KHz的信号也会有10%的误差
只有1KHz以下的信号才不会收到影响…………
连采集到的信号都不对,谈什么求和逼近积分……做梦吧,梦里啥都有……
结论三:普通方法不能求得1KHz以上信号的准确 真有效值
正所谓鱼和熊掌不可兼得……
但天无绝人之路,我还有DMA!!!意不意外,惊不惊喜!没想到吧!(此处自动脑补表情包)
可是我现在还不会……等我学会了再回来更新,就酱,如果本文对你有收获,想收藏就收藏,想点赞就点赞
未经允许,不得转载,谢谢
未经允许,不得转载,谢谢
未经允许,不得转载,谢谢
重要的事情说三遍!!!
2022 / 5 / 24 (突然 )更新……
(看我自己以前写的文章,感觉自己多少有点犯病,继续犯病ing)
前言
- 关于上文提到的测有效值的讨论,真有效值计算起来多少有些麻烦,但是利用ADC+DMA还是可以做到的,但是我也没再管过。最多用到AD637这块芯片测有效值,虽然不是真有效值,但应付毕设,课设还行,把复杂的事交给专业的吧,开摆!
- 最近重新捡起STM32,但是没有再用过keil了,而是用起了STM32CubeIDE,利用stm32cubeMX生成基础代码,省去基础配置时间,专心开发应用就行。而且也没再使用过std库了,HAL库确实很香。
- 下面介绍ADC连续采样+DMA双缓冲,实现DAC生成正弦波,ADC采样,并将波形回传至PC并绘制出来。其中DAC也使用了DMA功能,本篇不介绍(挖坑 )
- 硬件开发平台STM32F407ZET6开发板,时钟频率168MHz,APB1 Timer CLK:84MHz,APB2 Timer CLK:168MHz
预备知识
如果你没有接触过HAL库,那么下面有些词可能听的云里雾里的,这里做一个简单的说明
- 回调函数:对于一个函数A,我将函数B的函数指针(这个指针指向函数)做为参数传入到函数A中,那么我们称函数B是一个回调函数(Call Back Function)。这样我可以设置不同的回调函数B1,B2,B3……这意味着函数A对不同的函数Bx做出了相同的操作,因此才将其封装起来。当我需要的时候,我只需要传入不同的参数B1,B2,B3……就可以完成不同的操作。在HAL库中,我们往往要编写中断回调函数。当发生中断时,库函数会帮我们判断发生了什么中断,并完成清除标志位等操作,然后调用不同的中断回调函数(HAL_xxxCallback(…)),这些回调函数都是__weak函数,等待用户编写内容。
- __weak函数(弱函数):__weak关键字用来修饰函数或者变量,表示修饰的函数或变量的使用优先级较低。如果我们定义了一个函数或变量,它与关键字__weak修饰的函数或者变量名一致,则优先使用我们定义的,而忽略__weak修饰的。举个例子:
__weak int FuncCallback(int a, int b)
{//do nothingreturn 0;
}//如果我们不重新定义这个函数,那么编译器就会默认使用上面的那个__weak函数
//但如果我们重新定义了FuncCallback这个函数,那么编译器就会使用我们的
int FuncCallback(int a, int b)
{//user codereturn a + b;
}void UserFunc(void)
{int a = 2, b = 3;int retval;retval = FuncCallback(a, b);//2+3=5
}
DMA简介
DMA的传输数据不占用CPU,是外设(例如ADC或者DAC)到内存、内存到外设、内存到内存的一种传输数据手段。以ADC为例,当ADC转换完成后,由ADC向CPU发出DMA请求,CPU将总线权交给DMA,由DMA控制器将数据搬运到内存中,此时CPU可以干其它事情。等到DMA将一组数据搬运完成之后,我们就可以在中断函数里对数据做处理。
假如我们要计算真有效值,我们可以使用DMA先传输一组数据X1,然后CPU对数据X1做运算的同时,DMA开始接收下一组数据X2,等到X2接收完毕,CPU就可以对X2做处理。
DMA双缓冲
所谓的DMA双缓冲,即一个外设对应两个内存地址,记A和B两个内存地址,当从外设传输完一组数据到A内存后,CPU可以对此内存进行操作,同时开启外设到B内存的数据传输,反过来从内存到外设也是一样的,只是传输数据方向不同。这样CPU对内存的操作和DMA传输数据的操作就可以同时进行。
但是基于HAL库的cubeMX生成的代码对DMA双缓冲的支持并不好,相关的回调函数更是没有定义,用户需要自己设置。但这并不意味着没法实现DMA双缓冲,原因就在于HAL库有大量的HAL_xxxHalfCpltCallback()函数(传输一半回调函数——Half Complete Call Back)
如果我们使用第一种方法,那么我们需要定义两个数组A[10], B[10],假设长度都是10,那么也就是说,每当DMA传输10个数据到A或者B后,DMA会产生一个中断,CPU就可以处理这10个数据了。假如我们使用第二种方法,我们只需要定义数组C[20],那么当DMA传输完成一半的时候,也会有个中断,我们同样可以数据处理,开销是一样的。
ADC & DMA配置
- ADC单通道连续采样
- 12bits,数据右对齐
- 由软件触发(还可以设置为某些TIM的捕获/比较事件)
- DMA传输方向:由外设到内存,内存地址自动递增
- DMA模式:循环模式。即传输完一组数据后,内存地址重置,从头开始,覆盖原来的数据
- DMA传输数据宽度:半字(Half Word = 2 Byte = 16 bit)
- 采用DMA双缓冲,DMA一边传输数据,CPU一边将数据通过串口发送到PC,由PC绘制波形
具体配置如下图
DMA设置如下(使用DMA的时候,cubeMX强制开启对应中断,如有必要,用户在NVIC中配置优先级即可)
代码配置如下
//File adc.c
……
extern uint16_t ADC_RX_BUF[100];//以100个数据为一组
/* USER CODE BEGIN 1 *///DMA半传输完成回调函数
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{uint8_t i;if(hadc->Instance == ADC1){for(i = 0; i < 50; i++)//处理一半数据{printf("$%.5f;\n", 3.3 * ADC_RX_BUF[i] / 4095);//发送数据到pc,上位机将数据绘制出来,波特兰设置为460800}}
}//DMA传输完成回调函数
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{uint8_t i;if(hadc->Instance == ADC1){for(i = 50; i < 100; i++)//处理另一半数据{printf("$%.5f;\n", 3.3 * ADC_RX_BUF[i] / 4095);}}
}
/* USER CODE END 1 */
……
//File main.c
……
uint16_t ADC_RX_BUF[100];int main(void)
{//启动ADC,使能DMA,数据长度为100HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_RX_BUF, 100);while(1){}
}
……
实验结果如下
STM32之ADC的学习心得(std+HAL)相关推荐
- STM32学习心得二十六:DAC数模转换实验
记录一下,方便以后翻阅~ 主要内容: 1) DAC数模转换原理: 2) 寄存器和库函数介绍: 3) 相关实验代码解读. 实验功能:系统启动后,按WK_UP键,输出电压加200点,对应电压值200*3. ...
- STM32学习心得:SPI-Flash-W25Q16DV
前言 目前正在学习STM32单片机的基础知识,通过库函数实现想要的一些功能.这篇文章主要介绍的是片外的Flash的操作,Flash的型号是W25Q16DV(芯片介绍在后面),通信的方式是SPI通信. ...
- STM32学习心得二十四:内部温度传感器原理及实验
记录一下,方便以后翻阅~ 主要内容: 1) STM32内部温度传感器概述: 2) 相关实验代码解读. 实验功能:系统启动后,实时将内部温度传感器的值传至串口助手上. 官方资料:<STM32中文参 ...
- 10天学会STM32的学习心得总结
01 前言 有读者问,如何系统地入门学习stm32呢? 假如你会使用8051 , 会写C语言,那么STM32本身并不需要刻意的学习. 我们要考虑的是, 我可以快速用STM32实现什么? 为什么使用 ...
- STM32学习心得十九:电容触摸按键实验及相关代码解读
记录一下,方便以后翻阅~ 主要内容 1) 电容触摸按键原理: 2)部分实验代码解读. 实验内容 手触摸按键后,LED1灯翻转. 硬件原理图 上图,TPAD与STM_ADC用跳线帽相连,即TPAD与PA ...
- STM32学习心得二十一:实时时钟RTC和备份寄存器BKP特征、原理及相关实验代码解读
记录一下,方便以后翻阅~ 主要内容 1) RTC特征与原理: 2) BKP备份寄存器特征与原理: 3) RTC常用寄存器+库函数介绍: 4) 相关实验代码解读. 实验内容: 因为没有买LCD屏,所以计 ...
- STM32学习心得十八:通用定时器基本原理及相关实验代码解读
记录一下,方便以后翻阅~ 主要内容: 1) 三种定时器分类及区别: 2) 通用定时器特点: 3) 通用定时器工作过程: 4) 实验一:定时器中断实验补充知识及部代码解读: 6) 实验二:定时器PWM输 ...
- STM32学习心得十七:窗口看门狗(WWDG)实验及旧知识点复习
记录一下,方便以后翻阅~ 主要内容: 1) 窗口看门狗概述: 2) 常用寄存器和库函数配置: 3) 窗口看门狗实验. 窗口看门狗实验内容: 为了对之前的知识进行总结复习,本人在教学案例的基础上又&qu ...
- 使用HAL库开发STM32:ADC基础使用
文章目录 目的 基础说明 基础使用 配置选项说明 轮询 单通道 单次 轮询 单通道 连续 轮询 多通道 扫描 中断 单通道 中断 多通道 扫描 DMA 单通道 单轮 DMA 单通道 连续 DMA 多通 ...
最新文章
- linux 为开发板添加板级文件config.h
- 利用koa实现mongodb数据库的增删改查
- Visual Studio 2017 15.5.0 正式发布 正式版下载
- [C++][线程安全]单例模式下双检查锁和线程
- 嵌入式Linux系统之I.MX6触摸屏驱动程序TSC2007.C的分析、移植与校准
- react api_如何在WordPress REST API之上构建React应用
- 2020.xilinx开发环境
- Linux查询命令man手册各章节解释
- f2fs解析(四)f2fs的extent特性
- 中国1km分辨率的DEM数据以及合并后的中国行政区划数据
- WireGuard简单配置
- Leetcode 120. Triangle 三角形问题(动态规划经典) 解题报告
- cad2017单段线_cad2017新功能介绍
- C++常用的音频工具库
- 【译】将字符转换为双精度浮点型
- 确定电气间隙和爬电距离
- Android设备获取mp3中的专辑封面信息
- Java 生成 OFD 文档
- vscode报错之 对修饰器的实验支持功能在将来的版本中可能更改。在 “tsconfig“ 或 “jsconfig“ 中设置 “experimentalDecorators“ 选项以删除此警告。
- 微信公众号config:invalid signature签名失效