当前位置: 首页 > news >正文

嵌入式开发STM32 -- 江协科技笔记

1.背景介绍及基础认知

8大输入输出

斯密特触发器:高于设定阈值输出高电平,低于设定阈值输出低电平

有关上拉输入、下拉输入、推挽输出、开漏输出、复用开漏输出、复用推挽输出以及浮空输入、模拟输入的区别

1、上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将IO口上不确定的信号通过一个上拉电阻把IO上拉为高电平!电阻同时起限流作用!弱强只是上拉电阻的阻值不同,没有什么严格区分。

2、下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。

3、浮空输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。 通俗讲就是让管脚什么都不接,浮空着。

4、模拟输入:模拟输入是指传统方式的输入.数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。

5、推挽输出:可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

6、开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

7、复用开漏输出、复用推挽输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。

在STM32中选用IO模式,下面是参考网上的总结一下。
(1) 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别,RX
(2)带上拉输入_IPU——IO内部上拉电阻输入
(3)带下拉输入_IPD—— IO内部下拉电阻输入
(4) 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电
(5)开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能。
(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的
(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)

(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)

1.1.内核划分

A: 应用在手机 R: 实时性很高的领域 M: 应用在单片机

1.2.外设

1.3.在线安装支持包

2.新建工程文件

2.1.正确插线

2.2.添加必要的工程文件

2.2.1.在工程文件下新建一个Start文件夹,讲必要的工程文件添加到此文件夹

2.2.2.添加头文件路径 

2.2.3.创建User 文件夹创建Main函数

2.2.4.添加库文件 :工程下创建Library文件夹,把库函数都添加进去,创建Library组,在添加Library下的现有项

2.2.5.复制到工程User文件夹,再在Keil下添加现有项

2.2.6.定义宏配置工程文件

2.2.7.添加library和User头文件的路径

2.2.8.如何选择启动文件 

2.2.9.新建工程的步骤 

2.2.10.工程架构

3.GPIO

3.1.元气件两端有高低电平才可以形成电流:上:单片机低电平形成电流 下:单片机高电平形成电流

3.2.二极管:PN结

二极管有颜色的一极为负极:P为正极,N为负极;电流只能从正极流到负极,电子只能从负极客流到正极 

3.2.1.整流桥

3.3.三极管:NPN

PNP:

3.3.1.三极管发射极和集电极为什么不能互换

3.3.2.三极管连接的元器件位置(漏电问题):防止基极的小电流流过

3.3.3.开关电路

3.4.面包板的构造

3.5.清除工程文件

4.GPIO输入输出

4.1.RCC、GPIO最常用的3个函数

RCC简介:RCC是Reset and Clock Control (复位和时钟控制)的缩写,它是STM32内部的一个重要外设,负责管理各种时钟源和时钟分频,以及为各个外设提供时钟使能。RCC模块可以通过寄存器操作或者库函数来配置。

4.2.使用GPIO的函数点亮和熄灭灯

	//设置时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//输入低电平//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//输入高电平//GPIO_SetBits(GPIOA, GPIO_Pin_0);//输入低电平//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);//输入高电平GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);

4.3.使用延时函数达到使用灯闪烁的效果

4.3.1.在工程添加延时函数库

电路将LED接到A0

代码

int main(void)
{//设置时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);while(1){//输入低电平//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);Delay_ms(100);//输入高电平GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);Delay_ms(100);}return 0;
}

4.4.蜂鸣器

4.4.1.VCC/VSS/VEE等含义

一文读懂电路中VCC、VDD、VEE、VSS的区别 - 知乎 (zhihu.com)

在电子电路设计中,VCC、VDD、VEE和VSS是常见的电源和地线标识,它们各自代表不同的电源电压和地线类型。理解这些术语的区别对于正确设计和分析电路至关重要。

VCC

  • 定义:VCC是“Collector Voltage”或“Circuit Voltage”的缩写,通常用于双极型晶体管(如NPN晶体管)的集电极电源电压。在数字电路中,VCC通常指代正电源电压,用于为电路提供所需的正电压。
  • 应用:VCC广泛应用于模拟电路和数字电路中,提供电路所需的正电压。例如,在NPN晶体管电路中,VCC为正电压。
  • 特点:VCC通常用于模拟电源,提供稳定的电压。

VDD

  • 定义:VDD是“Drain Voltage”或“Device Voltage”的缩写,主要用于MOS晶体管和CMOS电路。它表示器件内部的工作电压。
  • 应用:VDD常见于集成电路(IC)和数字电路中,提供芯片的正电源电压。例如,在CMOS电路中,VDD通常连接到PMOS晶体管的源极。
  • 特点:VDD通常用于数字电源,提供芯片内部的工作电压。

VEE

  • 定义:VEE是“Emitter Voltage”或“Emitter-Emitter Voltage”的缩写,通常用于ECL电路的负电源电压。
  • 应用:VEE一般用于模拟电路中,提供负电源电压。例如,在PNP型晶体管电路中,VEE表示连接到发射极的电源电压。
  • 特点:VEE在放大电路中较为常见,用于提供对称电源。

VSS

  • 定义:VSS是“Source Voltage”或“Supply Voltage”的缩写,表示电源地或0V。
  • 应用:VSS通常用于数字电路中,作为电路的参考点或接地。在CMOS电路中,VSS指负电源。
  • 特点:VSS是电路的公共接地端,用于消除噪声和提供参考电平。

总结

  • 电压极性:VCC和VDD均表示正电源引脚,而VEE表示负电源引脚。VSS则代表接地引脚,不涉及电压极性。
  • 应用领域:VCC和VDD主要用于数字电路中,提供操作所需的正电压;VEE通常用于模拟电路中,提供负电压;VSS则用于连接电路到地,确保电路工作正常。
  • 传统用法:VCC和VSS这对术语通常用于晶体管和集成电路中;VDD和VEE则更常见于模拟电路设计中。

通过理解VCC、VDD、VEE和VSS的区别,可以更好地设计和调试电子设备和电路,确保电路的稳定性和可靠性。

代码:设置时钟 初始化对应接口 使用延时函数

	//蜂鸣器int main(){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);while(1){GPIO_ResetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_ResetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_13);Delay_ms(700);}return 0;}

4.4.分压电路

R1变小U0变大,R2变小U0变小

4.5.每一个开关控制一个LED的点亮和熄灭

线路图

 main

	//开关uint8_t keyNum;int main(){LED_Init();Key_Init();LED1_Off();LED2_Off();while(1){keyNum = GetKeyNum();if(keyNum == 1)LED1_turn();if(keyNum == 2)LED2_turn();}return 0;}

LED函数:LED取反:读取对应端口的高低电平,再设置取反的电平

#include "stm32f10x.h"   void LED_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);
}//LED的熄灭和点开
void LED1_ON()
{GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED1_Off()
{GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
void LED2_ON()
{GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED2_Off()
{GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED1_turn()
{//如果为低电平变为高电平if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)GPIO_SetBits(GPIOA, GPIO_Pin_1);	elseGPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED2_turn()
{//如果为低电平变为高电平if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)GPIO_SetBits(GPIOA, GPIO_Pin_2);	elseGPIO_ResetBits(GPIOA, GPIO_Pin_2);
}

Key函数:按下/松开开关会有抖动,使用延迟函数消除

#include "stm32f10x.h"   
#include "Delay.h"void Key_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = 	GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t GetKeyNum()
{uint8_t keyNum = 0;//LED2if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11 ) == 0 ){//消除抖动Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);//等松手//消除抖动Delay_ms(20);keyNum = 2;}//LED1if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 ) == 0 ){//消除抖动Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);//等松手//消除抖动Delay_ms(20);keyNum = 1;}return keyNum;
}

4.6. 光敏电阻控制蜂鸣器

电路图

main 

	//关敏电阻驱动蜂鸣器int main(){Buzzer_Init();LightSensor_Init();Buzzer_Off();while(1){if(GetLightSenNum() == 1)Buzzer_ON();elseBuzzer_Off();}return 0;}

Lightsensor 

#include "stm32f10x.h"   
#include "Delay.h"void LightSensor_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = 	GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}uint8_t GetLightSenNum()
{return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}

 Buzzer

#include "stm32f10x.h"   void Buzzer_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);
}void Buzzer_ON()
{GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_Off()
{GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_turn()
{//如果为低电平变为高电平if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)GPIO_SetBits(GPIOB, GPIO_Pin_12);	elseGPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

 4.7.OLED调试

OLED驱动函数

接线线路图

 4.6.1.调试

5.EXIT外部中断

5.1.中断的理论

不能使用pin相同的端口:例,PA1、PB1

5.2.对射式红外传感器

配置中断:初始化配置此4项

AFIO没有专门的库,内容和GPIO写在GPIO.h库内

NVIC函数在杂项文件内

main

	//对射式红外传感器int main(){CountSensor_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){uint16_t countSensor_Count = CountSensor_Get();OLED_ShowNum(1, 7, countSensor_Count, 5);}return 0;}

countSensor

#include "stm32f10x.h"    uint16_t countSensor_Count = 0;
void CountSensor_Init()
{//设置时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//初始化端口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,未检测到输入时默认为高电平GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输入不需要速度GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置外部中断引脚选择//配置EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line14;//那个端口EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉触发:遮挡时触发,上拉触发:遮挡移开后触发EXTI_Init(&EXTI_InitStructure);//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级NVIC_Init(&NVIC_InitStructure);
}void EXTI15_10_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line14) == SET)//检测14端口,是否发生异常{countSensor_Count++;EXTI_ClearITPendingBit(EXTI_Line14);//清除标志位,防止死循环}
}uint16_t CountSensor_Get()
{return countSensor_Count;
}

5.3.编码器计次

电路图

main

	int16_t encoderCount = 0;//编码器改变数值int main(){Encoder_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){encoderCount += EncoderNum_Get();OLED_ShowSignedNum(1, 7, encoderCount, 5);}return 0;}

encoder

#include "stm32f10x.h"    int16_t encoderNum = 0;
void Encoder_Init()
{//设置时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//初始化端口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,未检测到输入时默认为高电平GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输入不需要速度GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//配置外部中断引脚选择GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//配置EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//那个端口EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉触发:遮挡时触发,上拉触发:遮挡移开后触发EXTI_Init(&EXTI_InitStructure);//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选择通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选择通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级NVIC_Init(&NVIC_InitStructure);
}void EXTI0_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line0) == SET)//检测0端口,是否发生异常{if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)encoderNum--;EXTI_ClearITPendingBit(EXTI_Line0);//清除标志位,防止死循环}
}
void EXTI1_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line1) == SET)//检测1端口,是否发生异常{if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)encoderNum++;EXTI_ClearITPendingBit(EXTI_Line1);//清除标志位,防止死循环}
}uint16_t EncoderNum_Get()
{int16_t tmp = encoderNum;encoderNum =0;return tmp;
}

6.定时器

TIM ( Timer )定时器

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16 位计数器、预分频器、自动重装寄存器的时基单元,在 72MHz 计数时钟下可以实现最大 59.65s 的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入 捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

基础定时器:只有向上计时模式

通用定时器:向上计数、向下计数、中央对齐模式

6.1.定时器内部中断和定时器外部中断

time.h 

	#include "stm32f10x.h"  //定时器内部中断
//	void Time_Init()
//	{
//		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定时器1时钟开启
//		TIM_InternalClockConfig(TIM2);
//		
//		//初始化时基单位
//		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
//		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//时钟模式
//		TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//预分频器,此值和下值上限都为2^16
//		TIM_TimeBaseInitStructure.TIM_Prescaler =7200 - 1;//自动重装器
//		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计时器,高级定时器才需要
//		TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
//		
//		//清除一下标志位,防止一上电就进中断
//		TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//		
//		TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中断配置
//		
//		//NVIC中断配置
//		NVIC_InitTypeDef NVIC_InitStructure;
//		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
//		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//		NVIC_Init(&NVIC_InitStructure);
//		
//		TIM_Cmd(TIM2, ENABLE);//使能控制
//	}//定时器外部中断void Time_Init(){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定时器1时钟开启RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_Initstructure;GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0;GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//初始化时基单位TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//时钟模式TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//预分频器,此值和下值上限都为2^16TIM_TimeBaseInitStructure.TIM_Prescaler =1 - 1;//自动重装器TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计时器,高级定时器才需要TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);//清除一下标志位,防止一上电就进中断TIM_ClearFlag(TIM2, TIM_FLAG_Update);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中断配置//NVIC中断配置NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE);//使能控制}

main.h

	//时钟中断uint32_t num = 0; int main(){OLED_Init();Time_Init();while(1){OLED_ShowNum(1, 5, num, 5);OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);//获取自动重装值}return 0;}void TIM2_IRQHandler()//对应的中断函数{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET ){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);num++;}}	

6.2. PWM

6.2.1.PWM的基础概念

OC ( Output Compare )输出比较 

  • 输出比较可以通过比较 CNT(计数器) 与 CCR(捕获比较寄存器) 寄存器值的关系,来对输出电平进行置1 、置0或翻转的操作,用于输出一定频率和占空比的 PWM 波形
  • 每个高级定时器和通用定时器都拥有 4 个输出比较通道
  • 高级定时器的前 3 个通道额外拥有死区生成和互补输出的功能

计算一个频率为1KHZ、占空比为50%、分辨率为1%的ARR、CCR、PSC 

6.2.2.呼吸灯
#include "stm32f10x.h"void PWM_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//选择时钟TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High极性不翻转 TIM_OCPolarity_Low极性翻转TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能TIM_OCInitStructure.TIM_Pulse = 100; //CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}//修改CCR的值
void PWM_SetCompare1(uint32_t compare)
{TIM_SetCompare1(TIM2, compare);
}
	void LightClink(int ms1, int ms2){PWM_SetCompare1(100);Delay_ms(ms1);PWM_SetCompare1(0);Delay_ms(ms2);}//时钟内部/外部中断int main(){PWM_Init();while(1){LightClink(2000, 1000);LightClink(2000, 1000);LightClink(2000, 1000);LightClink(1000, 1000);LightClink(1000, 1000);//			for(int i = 0; i <=100; i++)
//			{
//				PWM_SetCompare1(i);
//				Delay_ms(40);
//			}
//			PWM_SetCompare1(0);
//			Delay_ms(40);
//			for(int i =0; i < 100 ; i++)
//			{
//				PWM_SetCompare1(100 - i);
//				Delay_ms(40);
//			}
//			PWM_SetCompare1(100);
//			Delay_ms(20);}return 0;}
6.2.3.端口重映射
	//重映射端口RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO);GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);

6.2.4.PWM操作舵机

void PWM_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//选择时钟TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStrucure.TIM_Period = 20000 - 1;TIM_TimeBaseInitStrucure.TIM_Prescaler = 72 - 1;TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);//输出比较TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OC2Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}//修改CCR的值
void PWM_SetCompare2(uint16_t compare)
{TIM_SetCompare2(TIM2, compare);
}
void Set_Angle(int angle)
{PWM_SetCompare2(angle * 2000 / 180 + 500);
}

	uint8_t keyNum ;int angle;//时钟内部/外部中断int main(){PWM_Init();Key_Init();OLED_Init();while(1){keyNum = GetKeyNum();if(keyNum == 1){angle += 30;if(angle > 180)angle = 0;}Set_Angle(angle);OLED_ShowString(1, 1, "angle:");OLED_ShowNum(1, 7, angle, 3);OLED_ShowString(2, 1, "num:");OLED_ShowNum(2, 5, keyNum, 1);}return 0;}
6.4.3.PWM操作电机

	int8_t speed;int8_t keyNum;//PWM操作电机int main(){PWM_Init();Key_Init();OLED_Init();while(1){keyNum = GetKeyNum();if(keyNum == 1){speed += 20;if(speed > 100) speed = -100;Set_MotorSpeed(speed);}OLED_ShowString(1,1,"Speed:");OLED_ShowSignedNum(1, 7, speed, 4);}return 0;}
void PWM_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//选择时钟TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStrucure.TIM_Period = 100 - 1;TIM_TimeBaseInitStrucure.TIM_Prescaler = 36 - 1;TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);//输出比较TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OC3Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}//修改CCR的值
void PWM_SetCompare3(uint16_t compare)
{TIM_SetCompare3(TIM2, compare);
}
void Set_MotorSpeed(int8_t speed)
{if(speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(speed);}else{GPIO_SetBits(GPIOA, GPIO_Pin_5);GPIO_ResetBits(GPIOA, GPIO_Pin_4);PWM_SetCompare3(-speed);//speed当前小于0		}
}
6.4.4. 输入捕获测频率和占空比

输入捕获流程

输入捕获检测方法

PSC数值修改 

	//输入捕获测频率int main(){PWM_Init();IC_Init();OLED_Init();PWM_SetCompare1(0);PWM_SetPrescaler(720 - 1);OLED_ShowString(1, 1, "Freq:00000Hz");OLED_ShowString(2, 1, "Duty:00%");while(1){OLED_ShowNum(1, 6, GetFreq(), 5);OLED_ShowNum(2, 6, GetDuty(), 2);}return 0;}

#include "stm32f10x.h"void PWM_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//选择时钟TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High极性不翻转 TIM_OCPolarity_Low极性翻转TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能TIM_OCInitStructure.TIM_Pulse = 100; //CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);
}//输入捕获测频率和占空比void IC_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//选择时钟TIM_InternalClockConfig(TIM3);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//配置通道1TIM_ICInitTypeDef TIM_ICInitstructure;TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//边沿检测、极性选择;上沿触发TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//数据选择器,直连输入TIM_ICInit(TIM3, &TIM_ICInitstructure);//配置通道2TIM_PWMIConfig(TIM3, &TIM_ICInitstructure);//等同于下面的代码,函数内部进行ifelse判断TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//边沿检测、极性选择;上沿触发TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//数据选择器,交叉输入TIM_ICInit(TIM3, &TIM_ICInitstructure);	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//选择触发源TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//选择从模式TIM_Cmd(TIM3, ENABLE);
}//修改CCR的值
void PWM_SetCompare1(uint16_t compare)
{TIM_SetCompare1(TIM2, compare);
}
//修改PSC
void PWM_SetPrescaler(uint16_t prescaler)
{TIM_PrescalerConfig(TIM2, prescaler, TIM_PSCReloadMode_Immediate);
}//获取频率
uint32_t GetFreq()
{return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}//获取占空比
uint32_t GetDuty()
{return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}
6.4.5.编码器接口
		//输入捕获测频率int main(){Encoder_Init();OLED_Init();OLED_ShowString(1, 1, "CNT:");while(1){OLED_ShowSignedNum(1, 5, Get_Encoder(), 5);Delay_ms(1000);}return 0;}
void Encoder_Init()
{//打开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//配置通道1TIM_ICInitTypeDef TIM_ICInitstructure;TIM_ICStructInit(&TIM_ICInitstructure);TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;TIM_ICStructInit(&TIM_ICInitstructure);TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_Cmd(TIM3, ENABLE);
}int16_t Get_Encoder()
{int tmp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return tmp;
}

7.AD模数转换

7.1.AD的概念

4种转换模式

  • 单次转换, 非 扫 描模式
  • 连续转换,非扫描模式
  • 单次转换,扫描模式
  • 连续转换, 扫描模式

//	//AD
//	uint16_t ADValue;
//	float voltage;
//	int main()
//	{
//		OLED_Init();
//		OLED_ShowString(1 ,1 , "ADValue:");
//		OLED_ShowString(2 ,1 , "Voltage:0.00V");
//		AD_Init();
//		while(1)
//		{
//			ADValue = AD_GetValue();
//			voltage = (float)ADValue / 4095 * 3.3;//需先转为浮点数进行除法运算
//			
//			OLED_ShowNum(1, 9, ADValue, 5);
//			OLED_ShowNum(2, 9, voltage, 1);
//			OLED_ShowNum(2, 11, (uint16_t)( voltage * 100 ) % 100, 2);
//			
//		}
//		return 0;
//	}//多ADuint16_t ad1, ad2, ad3, ad4;int main(){OLED_Init();OLED_ShowString(1 ,1 , "ad1:");OLED_ShowString(2 ,1 , "ad2:");OLED_ShowString(3 ,1 , "ad3:");OLED_ShowString(4 ,1 , "ad4:");AD_Init();while(1){ad1 = AD_GetValue(ADC_Channel_0);ad2 = AD_GetValue(ADC_Channel_1);ad3 = AD_GetValue(ADC_Channel_2);ad4 = AD_GetValue(ADC_Channel_3);OLED_ShowNum(1, 5, ad1, 5);OLED_ShowNum(2, 5, ad2, 5);OLED_ShowNum(3, 5, ad3, 5);OLED_ShowNum(4, 5, ad4, 5);}return 0;}
#include "stm32f10x.h"//void AD_Init()
//{
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	
//	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz
//	
//	//配置GPIO口
//	GPIO_InitTypeDef GPIO_InitStructure;
//	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
//	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
//	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//	GPIO_Init(GPIOA, &GPIO_InitStructure);
//	
//	//规则组通道配置
//	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定
//	
//	//初始化ADC
//	ADC_InitTypeDef ADC_InitStructure;
//	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式
//	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
//	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发
//	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续模式
//	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式
//	ADC_InitStructure.ADC_NbrOfChannel = 1;
//	ADC_Init(ADC1, &ADC_InitStructure);
//	
//	//运行控制
//	ADC_Cmd(ADC1, ENABLE);
//	
//	//校准复位
//	ADC_ResetCalibration(ADC1);
//	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//	ADC_StartCalibration(ADC1);
//	while(ADC_GetCalibrationStatus(ADC1) == SET);
//}//uint16_t AD_GetValue()
//{
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
//	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待
//	return ADC_GetConversionValue(ADC1);//获取
//}//连续转换模式
//void AD_Init()
//{
//	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续模式
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//连续转换模式,只需要触发启动一次
//}//uint16_t AD_GetValue()
//{
//	return ADC_GetConversionValue(ADC1);//获取
//}//非连续非扫描实现多AD
void AD_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz//配置GPIO口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//规则组通道配置ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定//初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续模式ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);//运行控制ADC_Cmd(ADC1, ENABLE);//校准复位ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);
}uint16_t AD_GetValue(uint8_t ADC_Channel)
{ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待return ADC_GetConversionValue(ADC1);//获取
}

8.DMA ( Direct Memory Access )直接存储器存取

DMA数据转运

	//DMA数据转运uint8_t arr1[] = {0x12, 0x23, 0xa1, 0x3b};uint8_t arr2[4];int main(){OLED_Init();MyDMA_Init((uint32_t)arr1, (uint32_t)arr2, 4);//数据转运while(1){OLED_ShowString(1, 1, "arr1:");OLED_ShowString(3, 1, "arr2:");OLED_ShowNum(1, 7, (uint32_t)arr1, 8); OLED_ShowNum(3, 7, (uint32_t)arr1, 8); OLED_ShowHexNum(2, 1, arr1[0], 2);OLED_ShowHexNum(2, 4, arr1[1], 2);OLED_ShowHexNum(2, 7, arr1[2], 2);OLED_ShowHexNum(2, 10, arr1[3], 2);Delay_s(1);	MyDMA_Transfer();arr1[0]++;arr1[1]++;arr1[2]++;arr1[3]++;OLED_ShowHexNum(4, 1, arr2[0], 2);OLED_ShowHexNum(4, 4, arr2[1], 2);OLED_ShowHexNum(4, 7, arr2[2], 2);OLED_ShowHexNum(4, 10, arr2[3], 2);Delay_s(1);}return 0;}
#include "stm32f10x.h"//ADC数据转移
uint32_t mySize;
void MyDMA_Transfer()
{DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA_SetCurrDataCounter(DMA1_Channel1, mySize);//修改计数器DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA_GetFlagStatus(DMA1_FLAG_TC1);//等待转运完成DMA_ClearFlag(DMA1_FLAG_TC1);
}
void MyDMA_Init(uint32_t addrA, uint32_t addrB, uint32_t size)
{//时钟开启RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = addrA;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryBaseAddr = addrB;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;mySize = size;DMA_InitStructure.DMA_BufferSize = mySize;//计数器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是来源还是目的地DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否使用软件触发DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否自动重装DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级DMA_Init(DMA1_Channel1, &DMA_InitStructure);//DMA使能DMA_Cmd(DMA1_Channel1, DISABLE);MyDMA_Transfer();
}
	//DMA数据转运ADCint main(){OLED_Init();MyDMA_Init();//数据转运OLED_ShowString(1 ,1 , "ad1:");OLED_ShowString(2 ,1 , "ad2:");OLED_ShowString(3 ,1 , "ad3:");OLED_ShowString(4 ,1 , "ad4:");while(1){
//			ADDMA_GetValue();OLED_ShowNum(1, 5, AD_Value[0], 5);OLED_ShowNum(2, 5, AD_Value[1], 5);OLED_ShowNum(3, 5, AD_Value[2], 5);OLED_ShowNum(4, 5, AD_Value[3], 5);}return 0;}

uint16_t AD_Value[4];void MyDMA_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//时钟开启RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz//配置GPIO口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//规则组通道配置ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);//初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续模式ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式ADC_InitStructure.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStructure);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_BufferSize = 4;//计数器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是来源还是目的地DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//是否使用软件触发DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//是否自动重装DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级DMA_Init(DMA1_Channel1, &DMA_InitStructure);//不可以在未使能前校准复位
//		//校准复位
//	ADC_ResetCalibration(ADC1);
//	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//	ADC_StartCalibration(ADC1);
//	while(ADC_GetCalibrationStatus(ADC1) == SET);//DMA使能DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);//开启DMA到ADC信号//ADC使能ADC_Cmd(ADC1, ENABLE);//校准复位ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);//自动转换转运的情况下使用ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
}
void ADDMA_GetValue(void)
{DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA_SetCurrDataCounter(DMA1_Channel1, 4);//修改计数器DMA_Cmd(DMA1_Channel1, ENABLE);//使能ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动//等待转运完成while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);DMA_ClearFlag(DMA1_FLAG_TC1);
}

9.串口通信(USART) 

9.1.基础概率

9.2.串口通信

使用printf函数,先打开Use MicroLIB

//此函数是printf函数底层调用打印的函数
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}	//串口int main(){Serial_Init();//printf函数移植方法printf("Num = %d\r\n",666);char str[100];sprintf(str, "Num = %d\r\n", 666);//指定打印位置Serial_SendString(str);while(1){}return 0;}

 串口接收

1.查询

	//串口发送int main(){Serial_Init();OLED_Init();while(1){//查询if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){receiveData = USART_ReceiveData(USART1);//读取后标志位自动置空OLED_ShowHexNum(1, 1, receiveData, 2);}}return 0;}

2.中断

	//串口发送int main(){Serial_Init();OLED_Init();while(1){//中断if(Serial_GetRxFLag() == 1){receiveData = Serial_GetRxData();//读取后标志位自动置空OLED_ShowHexNum(1, 1, receiveData, 2);//回传Serial_SendByte(receiveData);}}return 0;}
uint8_t serial_RxData,serial_RxFlag;uint8_t Serial_GetRxFLag(void)
{if(serial_RxFlag == 1){serial_RxFlag = 0;return 1;}return 0;
}uint8_t Serial_GetRxData()
{return serial_RxData;
}//串口接收和发送
void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效验位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bitUSART_Init(USART1, &USART_InitStructure);//中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE);
}void USART1_IRQHandler(void)
{if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){serial_RxFlag = 1;serial_RxData = USART_ReceiveData(USART1);}
}

9.3.串口通信数据包·

数据包

串口发送HEX数据包

	uint8_t receiveData;//串口发送HEX数据包int main(){Serial_Init();OLED_Init();Key_Init();serial_TxPacket[0] = 0x12;serial_TxPacket[1] = 0x23;serial_TxPacket[2] = 0x34;serial_TxPacket[3] = 0x45;uint8_t keyNum;//按键while(1){keyNum = GetKeyNum();if(keyNum == 1){serial_TxPacket[0]++;serial_TxPacket[1]++;serial_TxPacket[2]++;serial_TxPacket[3]++;Serial_SendPacket();OLED_ShowHexNum(2, 1, serial_TxPacket[0], 2); OLED_ShowHexNum(2, 4, serial_TxPacket[1], 2); OLED_ShowHexNum(2, 7, serial_TxPacket[2], 2); OLED_ShowHexNum(2, 10, serial_T xPacket[3], 2); }if(Serial_GetRxFLag()){OLED_ShowHexNum(1, 1, serial_RxPacket[0], 2); OLED_ShowHexNum(1, 4, serial_RxPacket[1], 2); OLED_ShowHexNum(1, 7, serial_RxPacket[2], 2); OLED_ShowHexNum(1, 10, serial_RxPacket[3], 2); }}return 0;}
//串口发送HEX数据包uint8_t serial_RxPacket[4], serial_TxPacket[4];
uint8_t serial_Flag;uint8_t Serial_GetRxFLag(void)
{if(serial_Flag == 1){serial_Flag = 0;return 1;}return 0;
}
//发送数据包
void Serial_SendPacket()
{Serial_SendByte(0xFF);//报头Serial_SendArray(serial_TxPacket, 4);Serial_SendByte(0xFE);//报尾
}
//接受数据包//串口接收和发送
void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效验位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bitUSART_Init(USART1, &USART_InitStructure);//中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE);
}void USART1_IRQHandler(void)
{if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){static uint8_t serial_State = 0;//当前状态static uint8_t pRxData = 0;//接受到第几个数据uint8_t rxData = USART_ReceiveData(USART1);//读取当前数据if(serial_State == 0){if(rxData == 0xFF){serial_State = 1;}}else if (serial_State == 1){serial_RxPacket[pRxData] = rxData;pRxData++;if(pRxData == 4){pRxData %= 4;serial_State = 2;}}else if(serial_State == 2){if(rxData == 0xFE){serial_State = 0;//状态改变serial_Flag = 1;//标志位可读}}}
}

串口发送文本数据包

//串口发送文本数据包char serial_RxPacket[100];
uint8_t serial_Flag;uint8_t Serial_GetRxFLag(void)
{if(serial_Flag == 1){serial_Flag = 0;return 1;}return 0;
}
//接受数据包//串口接收和发送
void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效验位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bitUSART_Init(USART1, &USART_InitStructure);//中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE);
}void USART1_IRQHandler(void)
{if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){static uint8_t serial_State = 0;//当前状态static uint8_t pRxData = 0;//接受到第几个数据uint8_t rxData = USART_ReceiveData(USART1);//读取当前数据if(serial_State == 0){if(rxData == '@'){serial_State = 1;pRxData = 0;}}else if (serial_State == 1){if(rxData == '\r')serial_State = 2;else{serial_RxPacket[pRxData] = rxData;pRxData++;}}else if(serial_State == 2){if(rxData == '\n'){serial_State = 0;//状态改变serial_RxPacket[pRxData] = '\0';//c串以'\0'结尾serial_Flag = 1;//标志位可读}}}
}
	uint8_t receiveData;//串口发送文本数据包int main(){Serial_Init();OLED_Init();LED_Init();Key_Init();OLED_ShowString(1, 1, "TxPacket:");OLED_ShowString(3, 1, "RxPacket:");while(1){if(Serial_GetRxFLag()){OLED_ShowString(4, 1, "                ");OLED_ShowString(4, 1, serial_RxPacket);if(strcmp(serial_RxPacket, "LED_ON") == 0){LED1_ON();Serial_SendString("LED_ON\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_ON_OK");}else if(strcmp(serial_RxPacket, "LED_OFF") == 0){LED1_Off();Serial_SendString("LED_OFF\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_OFF_OFF");}else{Serial_SendString("ERROR_COMMOD\r\n");OLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "ERROR_COMMOD");}}}return 0;}

 10.I2C

10.I2C通信

指定地址读取/写入一片区域的时序

12.Unix时间戳

12.1.时间戳的基础概念

12.2.BKP、RTC概念

12.3.BKP读写数据

  • BKP掉电数据(主和备用电源不同时掉电)不丢失
uint16_t writeArray[] = {0x1234, 0x5678};
uint16_t readArray[2];//BKP读写数据int main(){OLED_Init();Key_Init();//初始化时钟PWR/BKPRCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);//备用电源访问使能OLED_ShowString(1, 1, "W:");OLED_ShowString(2, 1, "R:");uint8_t keyNum;while(1){keyNum = GetKeyNum();//按键按下,写入数据++并写入if(keyNum == 1){writeArray[0]++;writeArray[1]++;BKP_WriteBackupRegister(BKP_DR1, writeArray[0]);BKP_WriteBackupRegister(BKP_DR2, writeArray[1]);OLED_ShowHexNum(1, 3, writeArray[0], 4);OLED_ShowHexNum(1, 8, writeArray[1], 4);}//实时读取数据readArray[0] = BKP_ReadBackupRegister(BKP_DR1);readArray[1] = BKP_ReadBackupRegister(BKP_DR2);OLED_ShowHexNum(2, 3, readArray[0], 4);OLED_ShowHexNum(2, 8, readArray[1], 4);}return 0;}

 12.4.实时时间

	//读取RTCint main(){OLED_Init();MyRTC_Init();/*显示静态字符串*/OLED_ShowString(1, 1, "Date:XXXX-XX-XX");OLED_ShowString(2, 1, "Time:XX:XX:XX");OLED_ShowString(3, 1, "CNT :");OLED_ShowString(4, 1, "DIV :");while(1){struct tm time_data = MyRTC_ReadTime();							//RTC读取时间,最新的时间存储到MyRTC_Time数组中OLED_ShowNum(1, 6, time_data.tm_year, 4);		//显示MyRTC_Time数组中的时间值,年OLED_ShowNum(1, 11, time_data.tm_mon, 2);		//月OLED_ShowNum(1, 14, time_data.tm_mday, 2);		//日OLED_ShowNum(2, 6, time_data.tm_hour, 2);		//时OLED_ShowNum(2, 9, time_data.tm_min, 2);		//分OLED_ShowNum(2, 12, time_data.tm_sec, 2);		//秒OLED_ShowNum(3, 6, RTC_GetCounter(), 10);	//显示32位的秒计数器OLED_ShowNum(4, 6, RTC_GetDivider(), 10);	//显示余数寄存器}return 0;}
#include "stm32f10x.h"
#include <time.h>void MyRTC_SetTime(struct tm time_date)
{time_t time_cnt;		//定义秒计数器数据类型time_cnt = mktime(&time_date) - 8 * 60 * 60;	//调用mktime函数,将日期时间转换为秒计数器格式//- 8 * 60 * 60为东八区的时区调整RTC_SetCounter(time_cnt);						//将秒计数器写入到RTC的CNT中RTC_WaitForLastTask();							//等待上一次操作完成
}struct tm MyRTC_ReadTime(void)
{time_t time_cnt;		//定义秒计数器数据类型struct tm time_date;	//定义日期时间数据类型time_cnt = RTC_GetCounter() + 8 * 60 * 60;		//读取RTC的CNT,获取当前的秒计数器//+ 8 * 60 * 60为东八区的时区调整time_date = *localtime(&time_cnt);				//使用localtime函数,将秒计数器转换为日期时间格式time_date.tm_year += 1900;time_date.tm_mon += 1;return time_date;
}void MyRTC_Init(void)
{if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)			//通过写入备份寄存器的标志位,判断RTC是否是第一次配置//if成立则执行第一次的RTC配置{//初始化时钟,备用电源访问打开RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);//时钟源配置,等待时钟准备好RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//选择时钟源RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);//等待时钟同步和上一次写入程序结束RTC_WaitForSynchro();RTC_WaitForLastTask();//设定分频RTC_SetPrescaler(32768 - 1);//分频到1HZRTC_WaitForLastTask();//每次操作要等待上一次操作结束//设置起始值struct tm time_data;time_data.tm_year = 2025 - 1900;time_data.tm_mon = 5 -1 ;time_data.tm_mday = 19;time_data.tm_hour = 5;time_data.tm_min = 30;time_data.tm_sec = 15;MyRTC_SetTime(time_data);//RTC_SetCounter(1747599833);RTC_WaitForLastTask();//每次操作要等待上一次操作结束BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);			//在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置}else													//RTC不是第一次配置{RTC_WaitForSynchro();								//等待同步RTC_WaitForLastTask();								//等待上一次操作完成}
}

13.电源控制

13.1.基础理论

13.2.修改主频 

只读文件,修改权限

	//修改主频int main(){OLED_Init();OLED_ShowString(1, 1, "SYSCLK:");OLED_ShowNum(1, 8, SystemCoreClock, 8);while(1){OLED_ShowString(2, 1, "Running");Delay_ms(500);OLED_ShowString(2, 1, "       ");Delay_ms(500);}return 0;}

13.3.睡眠模式+串口收发

		uint8_t receiveData;//串口发送int main(){Serial_Init();OLED_Init();while(1){
//			//查询
//			if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
//			{
//				receiveData = USART_ReceiveData(USART1);
//				//读取后标志位自动置空
//				OLED_ShowHexNum(1, 1, receiveData, 2);
//			}//中断if(Serial_GetRxFLag() == 1){receiveData = Serial_GetRxData();//读取后标志位自动置空OLED_ShowHexNum(1, 1, receiveData, 2);//回传Serial_SendByte(receiveData);OLED_ShowHexNum(1, 1, receiveData, 2);}OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, "       ");Delay_ms(100);__WFI();}return 0;}

 13.4.停机模式

  • 进入停止模式后会使用HSI时钟,需要重新选择时钟
//对射式红外传感器 + 停止模式int main(){CountSensor_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){OLED_ShowNum(1, 7, CountSensor_Get(), 5);OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, "       ");Delay_ms(100);//进入停止模式RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);//进入停止模式后会使用HSI时钟,需要重新使用时钟SystemInit();}return 0;}

13.5.待机模式

            //进入待机模式,程序将从头开始执行,后续代码不在执行,这也是不用再选择开启主频的原因

	//对射式红外传感器 + 待机模式int main(){MyRTC_Init();//开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);OLED_Init();OLED_ShowString(1, 1, "CNT:");OLED_ShowString(2, 1, "ALR:");OLED_ShowString(3, 1, "ALRF:");uint32_t alarm = RTC_GetCounter() + 10;RTC_SetAlarm(alarm );//闹钟设置为10s后OLED_ShowNum(2, 6, alarm , 10);//使用wakeup唤醒PWR_WakeUpPinCmd(ENABLE);while(1){OLED_ShowNum(1, 6, RTC_GetCounter(), 10);OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);OLED_ShowString(4, 1, "Running");Delay_ms(100);OLED_ShowString(4, 1, "       ");Delay_ms(100);//进入待机模式,程序将从头开始执行,后续代码不在执行,这也是不用再选择开启主频的原因PWR_EnterSTANDBYMode();}return 0;}

14.看门狗

14.1.基础概念

独立看门狗 

窗口看门狗

WWDG 工作特性

  • 递减计数器 T[6:0] 的值小于 0x40 时, WWDG 产生复位
  • 递减计数器 T[6:0] 在窗口 W[6:0] 外被重新装载时, WWDG 产生 复位
  • 递减计数器 T[6:0] 等于 0x40 时可以产生早期唤醒中断( EWI ), 用于重装载计数器以避免 WWDG 复位:作用:复位前保护数据,关闭一些危险元器件
  • 定期写入 WWDG_CR 寄存器(喂狗)以避免 WWDG 复位

独立看门狗和窗口看门狗的区别

14.2.代码实现

不用开启时钟的原因 

	//独立看门狗int main(){OLED_Init();Key_Init();OLED_ShowString(1, 1, "IWDG test:");if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET){OLED_ShowString(2, 1, "IWDG RESET");Delay_ms(500);OLED_ShowString(2, 1, "          ");Delay_ms(100);RCC_ClearFlag();}else{OLED_ShowString(3, 1, "ELSE RESET");Delay_ms(500);OLED_ShowString(3, 1, "          ");Delay_ms(100);}IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//取消写保护IWDG_SetPrescaler(IWDG_Prescaler_16);IWDG_SetReload(2500 - 1);IWDG_ReloadCounter();//启动前先喂一次狗,第一次就是完整的时间IWDG_Enable();//启动看门狗会改变键寄存器,进入写保护while(1){GetKeyNum();//函数内有一个等放手的while函数,会导致堵塞IWDG_ReloadCounter();OLED_ShowString(4, 1, "FEED");Delay_ms(600);OLED_ShowString(4, 1, "    ");Delay_ms(200);}return 0;}

窗口看门狗

	//独立看门狗int main(){OLED_Init();Key_Init();OLED_ShowString(1, 1, "WWDG test:");if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET){OLED_ShowString(2, 1, "WWDG RESET");Delay_ms(500);OLED_ShowString(2, 1, "          ");Delay_ms(100);RCC_ClearFlag();}else{OLED_ShowString(3, 1, "ELSE RESET");Delay_ms(500);OLED_ShowString(3, 1, "          ");Delay_ms(100);}RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);WWDG_SetPrescaler(WWDG_Prescaler_8);WWDG_SetWindowValue(0x40 | 21);WWDG_Enable(0x40 | 54);//启动看门狗while(1){GetKeyNum();//函数内有一个等放手的while函数,会导致堵塞OLED_ShowString(4, 1, "FEED");Delay_ms(20);OLED_ShowString(4, 1, "    ");Delay_ms(20);WWDG_SetCounter(0x40 | 54);//喂狗}return 0;}

15. flash闪存

  • 使用flash闪存存储数据时会占用闪存,这是代码不在执行,因为程序存储器(闪存的一部分)存储着可执行程序

15.2.代码实现使用闪存读写

uint8_t KeyNum;					//定义用于接收按键键码的变量int main(void)
{/*模块初始化*/OLED_Init();				//OLED初始化Key_Init();					//按键初始化Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失/*显示静态字符串*/OLED_ShowString(1, 1, "Flag:");OLED_ShowString(2, 1, "Data:");while (1){KeyNum = GetKeyNum();		//获取按键键码if (KeyNum == 1)			//按键1按下{Store_Data[1] ++;		//变换测试数据Store_Data[2] += 2;Store_Data[3] += 3;Store_Data[4] += 4;Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失}if (KeyNum == 2)			//按键2按下{Store_Clear();			//将Store_Data的数据全部清0}OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据OLED_ShowHexNum(3, 6, Store_Data[2], 4);OLED_ShowHexNum(4, 1, Store_Data[3], 4);OLED_ShowHexNum(4, 6, Store_Data[4], 4);}
}

 

#include "stm32f10x.h"                  // Device headeruint32_t MyFLASH_ReadWord(uint32_t Address)
{return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}uint8_t MyFLASH_ReadByte(uint32_t Address)
{return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}void MyFLASH_EraseAllPages(void)
{FLASH_Unlock();					//解锁FLASH_EraseAllPages();			//全擦除FLASH_Lock();					//加锁
}void MyFLASH_ErasePage(uint32_t PageAddress)
{FLASH_Unlock();					//解锁FLASH_ErasePage(PageAddress);	//页擦除FLASH_Lock();					//加锁
}void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramWord(Address, Data);		//编程字FLASH_Lock();							//加锁
}void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{FLASH_Unlock();							//解锁FLASH_ProgramHalfWord(Address, Data);	//编程半字FLASH_Lock();							//加锁
}
#ifndef __MYFLASH_H
#define __MYFLASH_Huint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);#endif
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"#define STORE_START_ADDRESS		0x0800FC00		//存储的起始地址
#define STORE_COUNT				512				//存储数据的个数uint16_t Store_Data[STORE_COUNT];				//定义SRAM数组void Store_Init(void)
{/*判断是不是第一次使用*/if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)	//读取第一个半字的标志位,if成立,则执行第一次使用的初始化{MyFLASH_ErasePage(STORE_START_ADDRESS);					//擦除指定页MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);	//在第一个半字写入自己规定的标志位,用于判断是不是第一次使用for (uint16_t i = 1; i < STORE_COUNT; i ++)				//循环STORE_COUNT次,除了第一个标志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);		//除了标志位的有效数据全部清0}}/*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/for (uint16_t i = 0; i < STORE_COUNT; i ++)					//循环STORE_COUNT次,包括第一个标志位{Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);		//将闪存的数据加载回SRAM数组}
}void Store_Save(void)
{MyFLASH_ErasePage(STORE_START_ADDRESS);				//擦除指定页for (uint16_t i = 0; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,包括第一个标志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);	//将SRAM数组的数据备份保存到闪存}
}void Store_Clear(void)
{for (uint16_t i = 1; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,除了第一个标志位{Store_Data[i] = 0x0000;							//SRAM数组有效数据清0}Store_Save();										//保存数据到闪存
}

相关文章:

  • Engineering a direct k-way Hypergraph Partitioning Algorithm【2017 ALENEX】
  • springboot启动流程
  • 实验四:用户管理和sudo提权
  • 【Redis】3-Redis应用
  • 大语言模型(LLM)入门项目推荐
  • 【springMVC】springMVC学习系列一:springMVC的组件
  • 经典分类模型
  • C#编解码:Base64扩展类的实现与应用
  • 一、奋斗类(事业奋斗/梦想实现)
  • ACM Latex模板:合并添加作者和单位
  • 智能护航 安心畅游——AI智能监控系统解决方案
  • 双11、618大促要做什么?
  • 报错:ImportError: cannot import name ‘metadata‘ from ‘importlib‘
  • IAM角色访问AWS RDS For MySQL
  • Linux云计算训练营笔记day16(Linux周期性计划任务、Python)
  • Prometheus、Exporter 和 Grafana:性能分析铁三角
  • 两阶段法目标检测发展脉络
  • Python 实现简单车牌识别
  • 【01】大模型原理与API使用
  • 第2章(新)Day2 - Python基础入门
  • 2019一个网站开发要多少钱/来宾网站seo
  • 商业合作及运营方案/免费的关键词优化工具
  • 徐州建设工程招标信息网官网/外贸seo网站建设
  • 做网站百度一下/杭州排名优化公司
  • seo网站推广优化就找微源优化/百度如何免费推广
  • 南昌智能建站模板/网络推广平台哪家公司最好