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);
//初始化GPIO
GPIO_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);
//初始化GPIO
GPIO_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); else GPIO_ResetBits(GPIOA, GPIO_Pin_1); } void LED2_turn() { //如果为低电平变为高电平 if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0) GPIO_SetBits(GPIOA, GPIO_Pin_2); else GPIO_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; //LED2 if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11 ) == 0 ) { //消除抖动 Delay_ms(20); while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);//等松手 //消除抖动 Delay_ms(20); keyNum = 2; } //LED1 if( 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(); else Buzzer_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); else GPIO_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);//配置外部中断引脚选择 //配置EXTI EXTI_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); //配置NVIC NVIC_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); //配置EXTI EXTI_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); //配置NVIC NVIC_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^16 TIM_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); //初始化GPIO RCC_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; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC TIM_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); //初始化GPIO RCC_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); //初始化GPIO RCC_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); //初始化GPIO RCC_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); //初始化GPIO RCC_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; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC TIM_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); //初始化GPIO RCC_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; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //配置通道1 TIM_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); //配置通道2 TIM_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); //初始化GPIO RCC_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; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //配置通道1 TIM_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; // } //多AD uint16_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采样周期数值越小速度越快,数值越大数据转换越稳定 //初始化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(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数据转运ADC int 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); //初始化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 = 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;//报文8bit USART_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;//报文8bit USART_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;//报文8bit USART_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/BKP
RCC_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.实时时间
//读取RTC int 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);//分频到1HZ RTC_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 header uint32_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_H uint32_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(); //保存数据到闪存 }