W806-ADC-PWM-TIM尝鲜

本文使用环境:
电脑: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

生成海报
点赞 0

Mr·赵

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

基于STM32的指纹密码锁

设计简介: 本设计是基于单片机的指纹密码锁,主要实现以下功能: 矩阵按键输入密码,并通过按键显示*号可通过按键或手机开门密码可通过按键进行开门可通过蓝牙模块连接手机进行开门可通过指纹进

STM32串口发送接收数据

1.串口通信 我用的32是stm32f10x最小系统没有UART4和UART5 USART : 通用同步异步收发器 UART : 通用异步收发器 nRTS : 请求发送 nCTS : 请求接收 区别:USART指单片机的一个IO端

TCRT5000循迹模块原理及应用

前言 本文将讲述TCRT5000循迹模块的原理及应用。本文应用于STM32,对于使用循迹模块的你有一定的帮助。 以下是本篇文章的正文内容 一、TCRT5000循迹模块介绍 TCRT5000就是一个红外发射和接收器&#xff0