智能电风扇(stm32f103c8t6)(直流电机,热敏传感器)(TIM,ADC)

前言

我的毕业论文的课题


提示:以下是本篇文章正文内容,下面案例可供参考

一、热敏传感器计算温度(ADC采样单通道)

#include "stm32f10x.h"                  // Device header

#define T25 298.15    
#define B   3380

float  Vlue;
uint16_t AD_Value;

void AD_Init(void)  //PB1
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	RCC_ADCCLKConfig( RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef ADC1_GPIO_Initstruction;
	ADC1_GPIO_Initstruction.GPIO_Mode = GPIO_Mode_AIN;
	ADC1_GPIO_Initstruction.GPIO_Pin = GPIO_Pin_1;
	ADC1_GPIO_Initstruction.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB, &ADC1_GPIO_Initstruction);
	
	ADC_RegularChannelConfig( ADC1, ADC_Channel_9, 1,  ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC1_My_Initsture;
	ADC1_My_Initsture.ADC_ContinuousConvMode =ENABLE;// DISABLE;
	ADC1_My_Initsture.ADC_DataAlign = ADC_DataAlign_Right;
	ADC1_My_Initsture.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC1_My_Initsture.ADC_Mode = ADC_Mode_Independent;
	ADC1_My_Initsture.ADC_NbrOfChannel = 1;
	ADC1_My_Initsture.ADC_ScanConvMode = DISABLE;
	ADC_Init(ADC1, & ADC1_My_Initsture);
	
	ADC_Cmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	
	ADC_SoftwareStartConvCmd( ADC1, ENABLE);
}

uint16_t AD_GetValue(void) // 这里用的连续转换,非扫描模式
{
	//while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
	return ADC_GetConversionValue(ADC1);
}

double myLn(double a)
{
	int  N = 15;
	int k=0,nk=0;
	double x=0.0,xx=0.0,y=0.0;
	x=(a-1)/(a+1);
	xx = x*x;
	nk = 2*N+1;
	y = 1.0/nk;
	
	for(k = N;k>0;k--)
	{
		nk = nk -2;
		y = 1.0/nk+xx*y;
	}
	return 2.0*x*y;
}

float Get_Temperaturn(void )
{
	float r_f = 0.0,temp_f = 0.0;
	
	AD_Value = AD_GetValue();
	Vlue = (float)AD_Value/4095*3.3;
	
	r_f = (Vlue*10000)/(3.3-Vlue); // 计算该温度下的热敏电阻的阻值
	temp_f = 1/((myLn(r_f/10000))/B + 1/T25 ) - 273.15; 
	//根据那个啥开尔文公式算的该时刻的温度(已经减了273.15,所以是摄氏度单位)
	
	return temp_f;
}

看这个图就可以知道配置流程:
在这里插入图片描述
我总结了配置流程:

ADC:模拟-数字转换器
ADC可以将引脚上连续变化的模拟量转化为内存中存储的数字变量,
建立起模拟电路到数字电路的桥梁。

12位逐次逼近ADC,1us转换时间。

输入电压范围:0-3.3v,转换结果范围:0-4095

ADC有18个输入通道,可测量16个外部信号源(外部引脚)和两个
内部信号源(内部温度传感器和内部参考电压)

有规则组和注入组两个转换单元。

(模拟看门狗自动监测输入电压范围):监测温度,湿度,电压等,
都会涉及到一个阈值的操作,所以可以模拟看门狗来自动执行。
模拟看门狗可以监测指定的某些通道,当AD值高于设定上阈值或者
低于下阈值时,就会申请中断,然后在中断里面进行相应操作,这样
就不用手动去读取。(但是模拟看门狗监测阈值,当达到阈值会触发
中断,就需要考虑项目怎么做)

ADC1 ADC2,10个外部输入通道

ADC的时钟频率最大14MHz,通过APB2分频过来是72MHz,ADC预分频
器可以2、4、6、8分频,所以只能选择6分频–12mhz或者8分频–9mhz

规则组的4中转换模式:
1·单次转换,非扫描模式
2·连续转换,非扫描模式
3·单次转换,扫描模式
4·连续转换,扫描模式

单次/连续转换:单次转换就是触发一次转换一次,连续转换就是触发一
次,转换一次完了后不需要再触发,直接转换。

扫描/非扫描模式:非扫描模式就是只转换一个通道,扫描模式就是转换多通道。

**

ADC初始化流程:

**
1·开启ADC的RCC时钟,开启引脚的时钟
2· RCC_ADCCLKConfig(uint32_t RCC_PCLK2);ADC最大14mhz,所以6或
者8分频
3·引脚初始化:用模拟输入模式
4·选择规则组的输入通道
ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
单通道只需要调用一次,多通道就多次调用。
5·初始化ADC结构体。ADC_Mode:独立模式和双通道模式(独立模式就是ADC1
转化ADC1,ADC2转化ADC2互不打扰);right-右对齐;外部触发转换选择-None(不
使用外部触发,用软件触发);连续转换模式:是不是连续转换;扫描模式是:是不是扫描模式;
指定规则组里面转换通道数目。
6·开启ADC
7·校准ADC
(1·复位校准(寄存器置1);2·等待复位校准完成(校准完成寄存器置0);3·开始校准;4·等待校准完成)

8·获取转化数值
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//软件触发,ADC已经开始转换

while(ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) == RESET);
//读取规则组转换完成标准位,判断是不是转换完成,没有则等待转换完成。
// ADC_GetFlagStatus 0 -未完成 1-完成

return ADC_GetConversionValue(ADC_TypeDef* ADCx);

}

注意:上面是单次模式。如果需要连续模式(也就是只需要一次触发,然后后面不需要再触发了),
顾名思义就是在初始化的时候就直接触发一次(这里是软件触发)。
所以,需要在初始化ADC结构体时,将连续模式ENABLE,然后完成校准ADC后直接触发一次。
获取转化数值当中就不需要软件触发了,也不需要去等待转换完成,直接返回转换的数值。

**

再说计算的温度

**
Rt = R 乘 EXP(B 乘 (1/T1-1/T2))
对上面的公式解释如下:

  1. Rt 是热敏电阻在T1温度下的阻值;
  2. R是热敏电阻在T2常温下的标称阻值;
  3. B值是热敏电阻的重要参数;
  4. EXP是e的n次方;
  5. 这里T1和T2指的是K度即开尔文温度,K度=273.15(绝对温度)+摄氏度;

在这里插入图片描述
根据串联分压,知道总电压VCC 3.3v,热敏电阻的电压V2是adc采集后经过转换得到的,也是已知,
所以R1的电压就是VCC - V2 ,然后根据R1电阻10K,可以求得电路的电流 I ,所以热敏电阻的
电阻 就可以用电流电压比值,于是得到Rt。
参数R 和 B值都是热敏电阻的参数,根据自己买的器件决定哈,我的就是10k,3380。可以问卖家,
也可以自己网上查型号,然后参数就出来了。
这里还要注意,T2的单位是开尔文,所以室温25摄氏度的开尔文是273.15+25=298.15.
就只剩下T1是未知数,一元一次方程,带进去一算就欧克。

二、控制直流电机(timer的输出比较PWM)

PWM.c

 #include "stm32f10x.h"                  // Device header

// #define ....

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	//PA2 tim2µÄch3×öpwmÊä³ö
	GPIO_InitTypeDef MyLEDStruction;
	MyLEDStruction.GPIO_Mode = GPIO_Mode_AF_PP;//¸´ÓÃÍÆÍêÊä³ö
	MyLEDStruction.GPIO_Pin = GPIO_Pin_2;
	MyLEDStruction.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &MyLEDStruction);
	
	TIM_InternalClockConfig(TIM2);//Ñ¡Ôñtim2µÄʱÖÓԴΪÄÚ²¿Ê±ÖÓ¡£¿ÉÒÔ²»Ð´£¬ÒòΪĬÈϵľÍÊÇÄÚ²¿Ê±ÖÓ
	
	TIM_TimeBaseInitTypeDef TIM_TimebaseIniture;
	TIM_TimebaseIniture.TIM_ClockDivision = TIM_CKD_DIV1;  //Óëʱ»ùµ¥ÔªÃ»¶à´ó¹Øϵ¡£
	TIM_TimebaseIniture.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimebaseIniture.TIM_Period = 100-1;     //TIM_Period = ARR.
	TIM_TimebaseIniture.TIM_Prescaler = 720-1;   //TIM_Prescaler = PSC.
	TIM_TimebaseIniture.TIM_RepetitionCounter = 0;//Öظ´¼ÆÊýÆ÷£¬¸ß¼¶¶¨Ê±Æ÷²ÅÓУ¬ÕâÀïÖ±½Ó¸ø0.
	TIM_TimeBaseInit( TIM2,  &TIM_TimebaseIniture);
	
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//¸øÊä³ö±È½Ï½á¹¹Ì帳³õʼֵ£¬Ê¹ÆäÒ»¸ötim¶à·Êä³öpwm²¨Õý³£¡£ÎÞÏ¡Ææ¹Å¹ÖµÄ´íÎó
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputState = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_Pulse = 0;//CCR
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd( TIM2, ENABLE);//¿ªÆôtim2.

}

void PWM_TIM_SetCompare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2,  Compare);
}

Moter.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

#define   MoterA_In_GPIO       GPIOA 
#define   MoterA_In_GPIO_RCC   RCC_APB2Periph_GPIOA 
#define   MoterA_In_Pin1       GPIO_Pin_4 
#define   MoterA_In_Pin2       GPIO_Pin_5


void Moter_Init(void)
{
	GPIO_InitTypeDef MyLEDStruction;
	RCC_APB2PeriphClockCmd(MoterA_In_GPIO_RCC,ENABLE);
	
	MyLEDStruction.GPIO_Mode = GPIO_Mode_Out_PP;
	MyLEDStruction.GPIO_Pin = MoterA_In_Pin1 | MoterA_In_Pin2;
	MyLEDStruction.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(MoterA_In_GPIO, &MyLEDStruction);
	
	PWM_Init();
	
}

void Moter_Speed(int8_t speed)
{
	if(speed >= 0)//+
	{
		GPIO_SetBits(MoterA_In_GPIO, MoterA_In_Pin1);
		GPIO_ResetBits(MoterA_In_GPIO,  MoterA_In_Pin2);
		PWM_TIM_SetCompare3( speed);
	}
	else
	{
		GPIO_ResetBits(MoterA_In_GPIO, MoterA_In_Pin1);
		GPIO_SetBits(MoterA_In_GPIO,  MoterA_In_Pin2);
		PWM_TIM_SetCompare3( -speed);
	
	}
	
}

由于直流电机内部驱动电路,所以我们需要一个驱动模块来驱动它。
TB6612模块驱动直流电机。
在这里插入图片描述
在这里插入图片描述
主要的连个模块就是这样了。
接下来是主函数:

int main (void)
{
	Key_GPIO_Init();
	Moter_Init();
	AD_Init();
	OLED_Init();
	
	//Moter_Speed(30);
	
	OLED_ShowString(1, 1, "Smart desk lamp");
	OLED_ShowString(2, 1, "Mode:");
	OLED_ShowString(display_gear_line, 1, "GEAR:0");
	OLED_ShowString(display_temp_line, 1, "Temp:00.0C");

	while(1)
	{
		temp_num = Get_Temperaturn();
	  OLED_ShowNum(display_temp_line, 6, temp_num , 2);
	  OLED_ShowNum(display_temp_line, 9, (uint8_t)(temp_num*10)%10 , 1);
		
		key_num = Get_KeyNum();
		
		if(key_num == 3)
		{
			key_change_flag = !key_change_flag;
			tempSencor_change_flag = !tempSencor_change_flag;
		}
		
		if(key_change_flag)   //手动
		{
			OLED_ShowString(2, 6, "Manual");
			if(key_num == 1)
			{
				speed_num += 30;
				gear_num ++;
				
				speed_num = speed_num >= 90 ? 90 : speed_num;
				gear_num = gear_num >= 3 ? 3 : gear_num;
				
				Moter_Speed( speed_num );
				OLED_ShowNum(display_gear_line, 6, gear_num , 1);
				
			}
			
			if(key_num == 2)
			{
				speed_num -= 30;
				gear_num --;
				
				speed_num = speed_num <= 0 ? 0 : speed_num;
				gear_num = gear_num <= 0 ? 0 : gear_num;
				
				Moter_Speed( speed_num );
				OLED_ShowNum(display_gear_line, 6, gear_num , 1);
				
			}
		}
	
		if(tempSencor_change_flag)  // 自动
		{
			OLED_ShowString(2, 6, "Automatic");
			TempSencor_Change_Moter();
		}
		
		// 自然风模式
		
	}

实验现象:
三个按键,k1,k2,k3.
k1增大风速,k2,减小风速。
k3切换手动模式,自动模式,自然风模式(电机线坏了,暂时没加)。

可以参考一下主函数去切换不同模式的方法。

总结

1· 32单片机跑裸机的时候,注意,只能有主函数里面的while(1)循环,其他地方不能出现死循环。我试过,跑不出来,崩溃了。
2· 模块化编程,一次最好循环一个模块。这个模块里面可以有一点点延时函数。其实延时函数对于整个系统来说是不友好的,我就
在想有没有一种方法可以计时,但是不吃主控的性能,我的初步想法就是用定时器,定时到了不是申请中断,而是申请事件。后面看
能不能试一下。

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值