本文使用环境:
电脑:windows10
主控:W806(240MHZ)
编译环境:平头哥的CDK
注意:本文默认已经搭建好平台。
前言
写这篇问章的目的是记录一下自己的ADC使用路程,前期这个ADC我是一直没有看明白,后面才搞的是懂非懂。
当然本文肯定不止使用ADC,那样显得没有技术水平,SO,为了提升技术难度,在adc的基础上加了一点点东西:
使用PB0产生100KHZ动态变化的的PWM,然后由PA1的ADC获取到,并转化成实际的电压。B0产生的PWM通过不断的改变比较值可以输出不同的电压,所以就能够满足ADC的采集要求,这里需要注意的是,不能直接测量电源电压,要不然要烧。。。。。
完整程序下载:
https://download.csdn.net/download/qq_37280428/48278788
一、编程
1. ADC编程
官方也有ADC的demo,所以这里主要说一下ADC的初始化以及,值读取出来后如何转化成实际的电压值。
具体的参考官方程序,
1.1、查端口
要编程之前我们首先要确定使用的外设IO,所以第一步打开官方数据手册,看看哪些口是可以使用的,打开如下文档。
直接ctrl+F,可以发现这个编号是非常奇怪的。
我们使用A1即可,默认也是A0所以问题不大,同时我们看一下文档中关于ADC的章节。
明确说明了,ADC最多只能是2.4V,所以,这就是之前说的不能直接接电源电压。。。。。。
同时查看ADC的参数信息(W806 MCU 芯片规格书 V2.0):这参数一般般
2、PWM编程
2.1、查手册
首先查手册,设计指导书和规格书都可以查,发现有很多的IO都是可以选择的,这里选择B0,PWM0。
除此之外,我们还需要打开一个手册:
翻到如下章节:
PWM时钟频率是3-160KHz,这都是后续需要设置的地方。
2.2、PWM编程
PWM的初始化需要特别注意,如果单独使用PWM0,使用官方的教程没有问题,但是如果你想使用 多路PWM,并且需要每一路单独输出PWM,就需要对PWM的初始化做修改。官方的初始化如下:(这个初始化不能完成每一路PWM单独输出)
static void PWM_Init(void)
{
// 输出100KHz、占空比40%的波形
hpwm.Instance = PWM;
hpwm.Init.AutoReloadPreload = PWM_AUTORELOAD_PRELOAD_ENABLE;
hpwm.Init.CounterMode = PWM_COUNTERMODE_EDGEALIGNED_DOWN;
hpwm.Init.Prescaler = 4;
hpwm.Init.Period = 99; // 40M / 4 / 100K - 1
hpwm.Init.Pulse = 0; // 0% DUTY
hpwm.Init.OutMode = PWM_OUT_MODE_5SYNC;
hpwm.Channel = ch;
HAL_PWM_Init(&hpwm);
}
其中Pulse 表示占空比,such as:Pulse 设置成0 就表示低电平,设置成Period /2就表示50%的占空比,设置成Period 就表示高电平,中间的就是高电平和低电平之比。动态改变这个值就可以输出可占空比波形。特别需要注意的是,由于ADC的输入电压不能高于2.4V所以,这里我做了限幅,在下面的完整程序中有设置,为什么是70? 3.3*0.7 = 2.31V 满足要求。
#define adc_max 70
同时这里需要注意两个点:
1、频率设置
系统默认输出的是100K的信号,那问题来了 这100K是如何设置的呢???
经过我的不断尝试,我发现,PWM的时钟输入默认是40MHz,所以这里直接按照40MHz,计算。这里直接给公式,自己去算。
输出频率 khz = 40000kHZ/4(分频系数)/(Period+1)
2、输出模式
观察上述代码的OutMode 设置的是PWM_OUT_MODE_5SYNC,字面意思,5通道同步模式,也就是说,每个通道输出的PWM都是一样的,如果单独输出需要修改为独立模式。按住ctrl然后单击PWM_OUT_MODE_5SYNC,即可跳转到宏定义的地方:
// PWM_Out_Mode
#define PWM_OUT_MODE_INDEPENDENT 0x00
#define PWM_OUT_MODE_2SYNC 0x01
#define PWM_OUT_MODE_2COMPLEMENTARY 0x02
#define PWM_OUT_MODE_5SYNC 0x03
#define PWM_OUT_MODE_BREAK 0x04
这是五种不同的模式,第一种就是独立,所以有需要单独使用没一路PWM的,可以直接改成第一种。
static void PWM_Init(void)
{
// 输出100KHz、占空比40%的波形
hpwm.Instance = PWM;
hpwm.Init.AutoReloadPreload = PWM_AUTORELOAD_PRELOAD_ENABLE;
hpwm.Init.CounterMode = PWM_COUNTERMODE_EDGEALIGNED_DOWN;
hpwm.Init.Prescaler = 4;
hpwm.Init.Period = 99; // 40M / 4 / 100K - 1
hpwm.Init.Pulse = 0; // 0% DUTY
hpwm.Init.OutMode = PWM_OUT_MODE_INDEPENDENT; //独立通道输出
hpwm.Channel = ch;
HAL_PWM_Init(&hpwm);
}
其他的完全按照教程走就可以了。
3、TIM编程
3.1、编程
这个编程不再赘述,按照官方demo出来就行。本文设置成==1000000US。==每1S改变一次PWM的值,改变一次输出电压。
4、 完整程序及计算方式
话不多说直接上程序。
#include <stdio.h>
#include "wm_hal.h"
#define ADC_Voltage_LSB 0.0013
#define adc_max 70 //PWM输出的最大值,按照周期100来算的。3.3*0.7 = 2.31
void Error_Handler(void);
//ADC INIT
static void ADC_Init(void);
ADC_HandleTypeDef hadc;
//PWM init
PWM_HandleTypeDef hpwm;
static void PWM_Init(void);
void Error_Handler(void);
uint32_t ch = PWM_CHANNEL_0;
//TIME init
TIM_HandleTypeDef htim0;
static void TIM0_Init(void);
int main(void)
{
SystemClock_Config(CPU_CLK_240M);
printf("enter main\r\n");
//使用通道0 B0
PWM_Init();
HAL_PWM_Start(&hpwm, ch);
//使用通道0 A0
ADC_Init();
//定时器 TIME0
TIM0_Init();
//在wm_it.c文件中,需要修改对应的参数
HAL_TIM_Base_Start_IT(&htim0);
while (1)
{
;
}
}
static void TIM0_Init(void)
{
htim0.Instance = TIM0;
htim0.Init.Unit = TIM_UNIT_US;
htim0.Init.Period = 1000000; //1s
htim0.Init.AutoReload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim0) != HAL_OK)
{
Error_Handler();
}
}
/* 输出波形的频率: f = 40MHz / Prescaler / (Period + 1);
* 输出波形的占空比:
* 沿对齐模式(递减):(Pulse + 1) / (Period + 1)
* Pulse >= Period:PWM输出一直为高电平
* Pulse < Period :PWM输出高电平宽度为(Pulse + 1),低电平宽度为(Period - Pulse)
* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(Period)
*
* 中间对齐模式 :(2 * Pulse + 1) / (2 * (Period + 1))
* Pulse > Period :PWM输出一直为高电平
* Pulse <= Period:PWM输出高电平宽度为(2 * Pulse + 1),低电平宽度为(2 * (Period - Pulse) + 1)
* Pulse = 0 :PWM输出高电平宽度为(1),低电平宽度为(2 * Period + 1)
*/
static void PWM_Init(void)
{
// 输出100KHz、占空比40%的波形
hpwm.Instance = PWM;
hpwm.Init.AutoReloadPreload = PWM_AUTORELOAD_PRELOAD_ENABLE;
hpwm.Init.CounterMode = PWM_COUNTERMODE_EDGEALIGNED_DOWN;
hpwm.Init.Prescaler = 4;
hpwm.Init.Period = 99; // 40M / 4 / 100K - 1
hpwm.Init.Pulse = 0; // 0% DUTY
hpwm.Init.OutMode = PWM_OUT_MODE_5SYNC;
hpwm.Channel = ch;
HAL_PWM_Init(&hpwm);
}
//1K的频率
static void ADC_Init(void)
{
hadc.Instance = ADC;
hadc.Init.channel = ADC_CHANNEL_0;
hadc.Init.freq = 1000;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{}
//可以理解成定时器服务函数
//1S
void HAL_TIM_Callback(TIM_HandleTypeDef *htim)
{
static int8_t adc_num = 1;
static bool adc_flage = 1;
if (htim->Instance == TIM0)
{
if(adc_num > adc_max ||adc_num <= 0)
adc_flage = !adc_flage;
if(0 == adc_flage)
adc_num++;
else
adc_num--;
HAL_PWM_Duty_Set(&hpwm, PWM_CHANNEL_0, adc_num);
int16_t value_2 = HAL_ADC_GET_INPUT_VOLTAGE(&hadc) - 14; //14ADC是对地测量值,这个值需要根据实际情况进行测量,我的板子是这么多,不代表你的板子也是。
//这一步是防止减出来是负值,会出错的
if(0 >= value_2)
value_2 = 0;
float V_actural = value_2*ADC_Voltage_LSB; //ADC_Voltage_LSB 表示实际电压和ADC数值之间的关系,还是比较线性的。
printf(" %d %0.2fV \r\n",value_2,V_actural);
}
}
void Error_Handler(void)
{
while (1)
{
}
}
void assert_failed(uint8_t *file, uint32_t line)
{
printf("Wrong parameters value: file %s on line %d\r\n", file, line);
}
程序中的ADC_Voltage_LSB表示实际电压和adc数值之间的线性系数 ADC_Voltage_LSB = 理论 / 实际adc读数。
这个系数是我多次测量得出来的结果。
第一列是万用表测出来PWM输出电压(PWM间隔10%,即0.33),第二列是ADC读出来的数值,第三列是理论上PWM的输出电压:当然这只是粗略值,看的出来也还行。
5、效果展示
电压是先变小再变大,然后再变小变大,依次循环。
版权声明:本文为CSDN博主「Mr·赵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37280428/article/details/121518619
暂无评论