本文使用环境:
电脑: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 #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 mainrn"); //使用通道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 rn",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 %drn", file, line); } 程序中的ADC_Voltage_LSB表示实际电压和adc数值之间的线性系数 ADC_Voltage_LSB = 理论 / 实际adc读数。 这个系数是我多次测量得出来的结果。 第一列是万用表测出来PWM输出电压(PWM间隔10%,即0.33),第二列是ADC读出来的数值,第三列是理论上PWM的输出电压:当然这只是粗略值,看的出来也还行。 5、效果展示 电压是先变小再变大,然后再变小变大,依次循环。
上一篇:W801/W800蓝牙收发数据与控制设计(一)-INDICATE
下一篇:联盛德W806+INA226 = 功率计
推荐阅读最新更新时间:2024-11-10 09:20
设计资源 培训 开发板 精华推荐
- 高效、低成本反激转换器,无需光耦反馈
- stem32
- LT4275CIDD IEEE 802.3af(类型 1)13W 受电设备的典型应用电路
- 使用 Microchip Technology 的 MIC2776L-YM5 的参考设计
- 使用 Microchip Technology 的 TC33163 的参考设计
- NCP691 1 A、低 IGND、极低压差稳压器 (VLDO) 的典型应用,带/不带固定版本的使能
- LT3710EFE LT3781EG LTC1698EGN 演示板,双路输出隔离电源,36V 至 72V 输入,3.3Vout @ 10A,1.8/2.5Vout @ 10A
- 使用 Diodes Incorporated 的 ZR78L057 的参考设计
- 【训练营】物联网时钟+陈海彬
- KA431SA 限流、电流源稳压器的典型应用