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

news2025/1/20 12:03:56

前言

我的毕业论文的课题


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

一、热敏传感器计算温度(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· 模块化编程,一次最好循环一个模块。这个模块里面可以有一点点延时函数。其实延时函数对于整个系统来说是不友好的,我就
在想有没有一种方法可以计时,但是不吃主控的性能,我的初步想法就是用定时器,定时到了不是申请中断,而是申请事件。后面看
能不能试一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/161384.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

看完这篇 教你玩转渗透测试靶机vulnhub——MONEYBOX: 1

Vulnhub靶机MONEYBOX: 1渗透测试详解Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;FTP匿名登入&#xff1a;③&#xff1a;SSH暴力破解④&#xff1a;…

Java操作Word模板产生全新内容Word

1. spire.doc的jar引用 首先我们需要用到国产word处理工具jar包spire.doc&#xff0c;可以通过maven仓库寻找&#xff0c;然后在pom文件中直接引用。 此处需要注意&#xff0c;我们需要使用的是spire.doc.free&#xff08;免费版的&#xff09;&#xff0c;切勿使用spire.doc&a…

c++ - 第23节 - C++的类型转换

1.C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与接收返回值类型不一致时&#xff0c;就需要发生类型转化&#xff0c;C语言中总共有两种形式的类型转换&#xff1a;隐式类型转换和…

业务安全情报 | 数十万元的数据报告,竟被50元批量转售

近期监测到某咨询公司针数据报告大量泄漏事件&#xff0c;该机构历年的数据报告以及近期更新的针对VIP会员的付费报告均在电商等渠道可以低价获取。 BSL-2022-a3c28号情报文件显示黑灰产通过作弊方式获取查看、下载权限&#xff0c;绕过限制将报告数据大量下载&#xff0c;并通…

javaEE初阶 — 认识文件

文章目录认识文件1. 树型结构组织和目录2. 文件路径&#xff08;Path&#xff09;2.1 绝对路径2.2 相对路径3. 文件的类型认识文件 文件分为 狭义 和 广义 两种 狭义的文件&#xff1a;指的是硬盘上的 文件 和 目录 广义的文件&#xff1a; 泛指计算机中很多的软硬件资源。操…

2022(一等奖)D926刘家峡库区潜在滑坡InSAR识别与分析

作品介绍 1 应用背景 滑坡是普遍存在于世界各地山区的主要灾害之一&#xff0c;严重威胁着人类的生命财产安全和自然环境。滑坡不但会直接破坏人类生命财产安全和建筑物&#xff0c;而且还会造成堰塞湖等次生灾害&#xff0c;进而对人类的生命财产安全和基础设施等造成二次破坏…

暴力破解 SSH

Kali 的 MSF 终端&#xff0c;对渗透目标主机 的 SSH 服务进行暴力破解。 破解的是否成功取决于字典和目标是否使用弱密码。 一&#xff0c;实验环境 分别是攻击机和靶机&#xff08;也可是其他目标服务器&#xff09; 二、利用 SSH 弱密码进行暴力破解 暴力破解最重要的要…

100 道 Linux 笔试题,能拿 80 分就算大神!

本套笔试题共100题&#xff0c;每题1分&#xff0c;共100分。&#xff08;参考答案在文章末尾&#xff09; 1.cron 后台常驻程序 (daemon) 用于&#xff1a; A. 负责文件在网络中的共享 B. 管理打印子系统 C. 跟踪管理系统信息和错误 D. 管理系统日常任务的调度 2.在大多数Li…

如何使用无标签数据进行预训练?

一、直观解释 简单来说就是“造目标”。也即人为地去构造一些子任务&#xff08;论文里的protext task&#xff09;&#xff0c;或者利用一些最基本的公理性常识&#xff0c;去设计“类似监督”的任务。所不同的是&#xff0c;我们引入的“类似监督”的任务通常是用来完成表征…

day13|559.n叉树的最大深度、222.完全二叉树的节点个数

559.n叉树的最大深度 给定一个 N 叉树&#xff0c;找到其最大深度。最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。N 叉树输入按层序遍历序列化表示&#xff0c;每组子节点由空值分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;r…

基于opencv的图像:边缘检测 (完整代码+详细教程)

给出“离散拉普拉斯算子”一般形式的数学推导 离散值的导数使用差分代替: 所以: 以(x, y)为中心点,在水平和垂直方向上应用拉普拉斯算子,滤波器(对应a=1的情况)为:

广告归因-让你彻底弄归因架构实现

这里会引用神策数据很多的介绍&#xff0c;然后进行总结 归因方法 自归因 渠道商帮我们做归因&#xff0c;有的是每个用户打开 app 都回传给渠道商&#xff0c;渠道商自己归因有的如华为是从应用商店安装时&#xff0c;应用商店把归因信息写入到 app, 然后首次安装启动时能从本…

【信息学CSP-J近16年历年真题64题】真题练习与解析 第12题之加工零件

加工零件 描述 凯凯的工厂正在有条不紊地生产一种神奇的零件,神奇的零件的生产过程自然也很神奇。工厂里有 𝑛 位工人,工人们从 1~𝑛编号。某些工人之间存在双向的零件传送带。保证每两名工人之间最多只存在一条传送带。 如果 𝑥 号工人想生产一个被加工到第 𝐿(…

回溯法--旅行售货员问题--排列树

回溯法有点类似于暴力枚举的搜索过程&#xff0c;回溯法的基本思想是按照深度优先搜索的策略&#xff0c;从根节点出发深度搜索解空间树&#xff0c;当搜索到某一节点时&#xff0c;如果该节点可能包含问题的解&#xff0c;则继续向下搜索&#xff1b;反之回溯到其祖先节点&…

【学习笔记】【Pytorch】六、nn.Module的使用

【学习笔记】【Pytorch】六、nn.Module的使用学习地址主要内容一、torch.nn模块概述二、nn.Module类的使用1.使用说明2.代码实现学习地址 PyTorch深度学习快速入门教程【小土堆】. 主要内容 一、torch.nn模块概述 概述&#xff1a;帮助程序员方便执行与神经网络相关的行为。…

微信小程序——WXSS模板样式,rpx,样式导入,全局样式和局部样式,全局配置-window

一.WXSS模板样式1.什么是 wxss WXSS ( WeiXin Style Sheets &#xff09;是一套样式语言&#xff0c;用于美化 WXML 的组件样式&#xff0c;类似于网页开发中的CSS。2. WXSS 和 CSS 的关系WXSS 具有 CSS 大部分特性&#xff0c;同时&#xff0c; WXSS 还对 CSS 进行了扩充以及修…

73. 风格迁移以及代码实现

摄影爱好者也许接触过滤波器。它能改变照片的颜色风格&#xff0c;从而使风景照更加锐利或者令人像更加美白。但一个滤波器通常只能改变照片的某个方面。如果要照片达到理想中的风格&#xff0c;可能需要尝试大量不同的组合。这个过程的复杂程度不亚于模型调参。 本节将介绍如…

数据导入导出(POI以及easyExcel)

一.概念&#xff1a; 1.场景需求 将一些数据库信息导出为Excel表格 将Excel表格数据导入数据库 大量数据的导入导出操作 常⽤的解决⽅案为&#xff1a;Apache POI与阿⾥巴巴easyExcel2.Apache POI介绍 Apache POI 是基于Office Open XML 标准&#xff08;OOXML&#xff09;和M…

96、【树与二叉树】leetcode ——404. 左叶子之和:递归法[先序+后序]+迭代法[先序+层次](C++版本)

题目描述 原题链接&#xff1a;404. 左叶子之和 解题思路 一、递归法 &#xff08;1&#xff09;设置一个布尔变量判定&#xff08;先序遍历&#xff09; 左孩子一定在每个子树的最左侧&#xff0c;因此设置一个局部参数flag&#xff0c;当每次遍历的是左子树时&#xff0c…

记录一次Oracle Linux7上安装RDBMS 11.2.0.4的问题

参考文档&#xff1a; 文档1 OHASD fails to start on SuSE 11 SP2 on IBM: Linux on System z (Doc ID 1476511.1) As part of the root.sh, roothas.pl or rootcrs.pl is called and an entry is placed in /etc/inittab h1:35:respawn:/etc/init.d/init.ohasd run >/de…