前言
我的毕业论文的课题
提示:以下是本篇文章正文内容,下面案例可供参考
一、热敏传感器计算温度(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))
 对上面的公式解释如下:
- Rt 是热敏电阻在T1温度下的阻值;
- R是热敏电阻在T2常温下的标称阻值;
- B值是热敏电阻的重要参数;
- EXP是e的n次方;
- 这里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· 模块化编程,一次最好循环一个模块。这个模块里面可以有一点点延时函数。其实延时函数对于整个系统来说是不友好的,我就
 在想有没有一种方法可以计时,但是不吃主控的性能,我的初步想法就是用定时器,定时到了不是申请中断,而是申请事件。后面看
 能不能试一下。

















![96、【树与二叉树】leetcode ——404. 左叶子之和:递归法[先序+后序]+迭代法[先序+层次](C++版本)](https://img-blog.csdnimg.cn/082c7acde9f64f88b4dc13da287cebbd.png)
