一、链接
1、stm32【按键处理:单击、连击、长按】_stm32长按短按 连续长按_Elven-C的博客-CSDN博客
2、复用和重映像的关系_复用功能的重映象_lwlwinner的博客-CSDN博客
3、GPIO输入输出模式原理(八种工作方式附电路图详解)_gpio输出模式_行稳方能走远的博客-CSDN博客
二、
2、1 工装制作(IAR)
(1)目的:
制作一个检测遥控器低功耗的检测装置
(2)逻辑:
上电即检测遥控器电流,电流>10uA,无源蜂鸣器滴一声,电流<10uA,无源蜂鸣器长鸣,按下按键后蜂鸣器停止响。
(3)思路:
a、GPIO口配置 Set_Gpio.c
#ifndef _GPIOSET_C
#define _GPIOSET_C
#include "sys.h"
#include "GPIOSet.h"
void GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA|RCC_AHBENR_GPIOB|RCC_AHBENR_GPIOC|RCC_AHBENR_GPIOD, ENABLE);//使能PORTA,PORTB,PORTC时钟
//BUZZ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//KEY
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
b、无源蜂鸣器用定时器配置(我用的是定时器2) Timer.c
#ifndef _TIM_C
#define _TIM_C
#include "TIM.h"
#include "sys.h"
void PWM_TIM2_Configuration(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB|RCC_AHBENR_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;//PB6 BUZZER
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // DIANJI|GPIO_Pin_1|GPIO_Pin_3|GPIO_Pin_13|GPIO_Pin_14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_6); //TIM1-CH2 LED PB4
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_4); //TIM2-CH1 BUZZ PB6
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0000;
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 1000-1; //计数周期,向上记到此数,计数值清零
TIM_TimeBaseStructure.TIM_Prescaler = 12-1;//48-1;//定时器分频系数,Ftimer = 72M/(TIM_Prescaler+1) = 1us
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//与死区时间分频有关
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel1 */
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;//比较互补输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //比较值,即占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//互补输出极性
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;//指定空闲状态下的TIM输出比较的引脚状态。
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;//指定空闲状态下的TIM互补输出比较的引脚状态。
TIM_OC1Init(TIM2,&TIM_OCInitStructure); //初始化通道二比较输出
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //配置通道二,自动重装载使能
TIM_ARRPreloadConfig(TIM2, ENABLE);//重载装载值 ENABLE 立即生效,DISABLE 下一个比较周期生效
TIM_Cmd(TIM2, ENABLE);//使能定时器2
TIM_CtrlPWMOutputs(TIM2, ENABLE);//使能PWM外围输出
}
void TIM2_Pulse(unsigned char channel,unsigned short Pulse)
{
switch(channel)
{
case TIM_CHANNEL1:
{
TIM2->CCR1 = (Pulse);
}break;
case TIM_CHANNEL2 :
{
TIM2->CCR2 =(Pulse);
}break;
case TIM_CHANNEL3:
{
TIM2->CCR3 = (Pulse);
}break;
case TIM_CHANNEL4 :
{
TIM2->CCR4 =(Pulse);
}break;
}
}
#endif
Timer.h
#ifndef _TIM_H
#define _TIM_H
#define SYS_CLOCK 48000000UL // 48MHz
#define PWM_FREQ ((unsigned short) 16000) // in Hz (N.b.: pattern type is center aligned)
#define DEADTIME_NS ((unsigned short) 1600) //in nsec (上升沿+下降沿 死区时间之和),2.25us
#define PWMCYCLE ((SYS_CLOCK / PWM_FREQ/2) - 1) // = 1199
#define MAXPWM PWMCYCLE // 0:pwm = 0%, MAXPWM: pwm = 100%
#define MINPWM 60
#define TIM_CHANNEL1 1
#define TIM_CHANNEL2 2
#define TIM_CHANNEL3 3
#define TIM_CHANNEL4 4
void PWM_TIM2_Configuration(void);
void TIM2_Pulse(unsigned char channel,unsigned short Pulse);
#endif
c、sys.c
#include "sys.h"
RCC_ClocksTypeDef Frequency;
volatile unsigned int Sys_DelayCnt=0;
void Systick_Init(void)
{
/**Configure the Systick interrupt time */
RCC_GetClocksFreq(&Frequency);
SysTick_Config(Frequency.SYSCLK_Frequency/1000);
// /* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, 0x03);//SysTickאԅЈܶʨ׃;
}
void SysDelay_ms(unsigned int delays)
{
Sys_DelayCnt=delays;
while(Sys_DelayCnt)
IWDG_ReloadCounter();
}
sys.h
#ifndef __SYS_H
#include "HAL_conf.h"
#include "HAL_device.h"
#include "stdio.h"
#include "dtype.h"
extern volatile unsigned int Sys_DelayCnt;
void Systick_Init(void);
void SysDelay_ms(unsigned int delays);
#endif
d、BUZZ.c
#ifndef _BUZZ_C
#define _BUZZ_C
#include "TIM.h"
#include "BUZZ.h"
BUZZ_Control BUZZ1_Control;
unsigned int BUZZ_Count1;//电流小于10uA 记时间
unsigned int BUZZ_Count2;//电流大于10uA 记时间
unsigned short Df_cnt;
unsigned short Scan_cnt;
//unsigned int Buzz_Continue;
unsigned char Flag_Scan=0;
void Buzz_init(void)
{
BUZZ1_Control.BUZZ_OldStatus=BUZZ_OFF;
BUZZ1_Control.BUZZ_Status=BUZZ_OFF;
BUZZ1_Control.BUZZ_Cnt=0;
BUZZ1_Control.BUZZ_Cnt1=0;
BUZZ1_Control.BUZZ_ControlStatus=0;
BUZZ1_Control.BUZZ_RunStatus=0;
}
void Buzz_Control(void)
{
if((BUZZ1_Control.BUZZ_Status!=BUZZ_IDLE))
{
if(BUZZ1_Control.BUZZ_Cnt==0)
{
BUZZ1_Control.BUZZ_Status=BUZZ_OFF;
}
if ((BUZZ1_Control.Buzz_Send_cnt<=4)&&(BUZZ1_Control.BUZZ_Cnt1==0))
{
switch (BUZZ1_Control.BUZZ_Buff[BUZZ1_Control.Buzz_Send_cnt])
{
case 1:
{
BUZZ1_Control.BUZZ_Status=BUZZ_ON;
BUZZ1_Control.BUZZ_Cnt=BUZZ_ShortTIME;
BUZZ1_Control.BUZZ_Cnt1=BUZZ_ShortTIME*2;
}break;
case 2:
{
BUZZ1_Control.BUZZ_Status=BUZZ_ON;
BUZZ1_Control.BUZZ_Cnt=BUZZ_LongTIME;
BUZZ1_Control.BUZZ_Cnt1=BUZZ_LongTIME*2;
}break;
case 3:
{
BUZZ1_Control.BUZZ_Status=BUZZ_ON;
BUZZ1_Control.BUZZ_Cnt=BUZZ_XLTIME;
BUZZ1_Control.BUZZ_Cnt1=BUZZ_XLTIME*2;
}break;
default :
{
BUZZ1_Control.BUZZ_Status=BUZZ_IDLE;
BUZZ1_Control.Buzz_Send_cnt=4;
}break;
}
BUZZ1_Control.Buzz_Send_cnt++;
}
}
if (BUZZ1_Control.BUZZ_Status!=BUZZ1_Control.BUZZ_OldStatus)
{
BUZZ1_Control.BUZZ_OldStatus= BUZZ1_Control.BUZZ_Status;
if (BUZZ1_Control.BUZZ_OldStatus==BUZZ_ON)
{
BUZZ_ON();
}
else if (BUZZ1_Control.BUZZ_OldStatus==BUZZ_OFF)
{
BUZZ_OFF();
}
}
if (BUZZ1_Control.Buzz_Send_cnt>4)//闲时
{
BUZZ1_Control.BUZZ_Status=BUZZ_IDLE;
BUZZ1_Control.Buzz_Send_cnt=0;
}
}
#endif
BUZZ.h
#ifndef _BUZZ_H
#define _BUZZ_H
#define BUZZ_ON() TIM2_Pulse(TIM_CHANNEL1,500);
#define BUZZ_OFF() TIM2_Pulse(TIM_CHANNEL1,0);
#define BUZZ_XXXLTIME 20000000
#define BUZZ_XLTIME 2000
#define BUZZ_LongTIME 500
#define BUZZ_ShortTIME 200
#define BUZZ_Compare 400
#define BUZZ_size 5
typedef enum
{
BUZZ_IDLE= 0,
BUZZ_ON= 1, //开
BUZZ_STON= 2,
BUZZ_OFF= 3, //开
BUZZ_STOFF=4, //关
// BUZZ_Toogle=5,
}BUZZ_Switch;
typedef struct
{
BUZZ_Switch BUZZ_Status;
BUZZ_Switch BUZZ_OldStatus;
unsigned char BUZZ_ControlStatus;//1蜂鸣1次
unsigned char BUZZ_RunStatus;//1蜂鸣1次
unsigned short BUZZ_Cnt;//f蜂鸣器蜂鸣计数器
unsigned short BUZZ_Cnt1;//蜂鸣器停止计数器
unsigned char Buzz_Send_cnt; //扫描次数
unsigned char BUZZ_Buff[BUZZ_size];
}BUZZ_Control;
extern BUZZ_Control BUZZ1_Control;
extern unsigned int BUZZ_Count1;
extern unsigned int BUZZ_Count2;
extern unsigned short Df_cnt;
extern unsigned short Scan_cnt;
//extern unsigned int Buzz_Continue;
extern unsigned char Flag_Scan;
void Buzz_init(void);
void Buzz_Control(void);
#endif
e、MMit.c
#ifndef _MM32F0XX_C
#define _MM32F0XX_C
/* Includes ------------------------------------------------------------------*/
#include "sys.h"
#include "mm32_it.h"
#include "BUZZ.h"
#include "ADC.h"
#include "KEY.h"
#include "TIM.h"
void SysTick_Handler(void)
{
// ADC_TVul=ADC_Value-ADC_Temp;
if(Sys_DelayCnt)
Sys_DelayCnt--;
if (BUZZ1_Control.BUZZ_Cnt1)
BUZZ1_Control.BUZZ_Cnt1--;
if (BUZZ1_Control.BUZZ_Cnt)
BUZZ1_Control.BUZZ_Cnt--;
if(KEY1_SCANF)
{
Key1_Mode.Key_PressCnt++;
}
else if (Key1_Mode.Key_Status!=NoClick)
{
Key1_Mode.Key_LossenCnt++;
}
if((ADC_Result>30)&&(Scan1_Flag1_Mode.BUZZ_Flag1==Scan_IDLE))
{
Scan_cnt++;//持续60毫秒以上 进入判断YES 、NO;
if(Scan_cnt>60)
{
Scan1_Flag1_Mode.BUZZ_Flag1=Scan_Start;
BUZZ_Count1=0;
BUZZ_Count2=0;
Scan_cnt=0;
}
}
else if ((ADC_Result<10)&&(ADC_Result>0)&&(Scan1_Flag1_Mode.BUZZ_Flag1!=Scan_IDLE))//检测电流 在55-65之间 持续500mS 判定为IDLE状态
{
Df_cnt++;
if (Df_cnt>=20)
{
Df_cnt=0;
Scan1_Flag1_Mode.BUZZ_Flag1=Scan_IDLE;
BUZZ_Count1=0;
BUZZ_Count2=0;
Scan_cnt=0;
}
}
if((ADC_Result<=400)&&(ADC_Result>20)&&(Scan1_Flag1_Mode.BUZZ_Flag1==Scan_Start))
{
BUZZ_Count1++;
BUZZ_Count2=0;
Df_cnt=0;
Scan_cnt=0;
}
else if((ADC_Result>500)&& (Scan1_Flag1_Mode.BUZZ_Flag1==Scan_Start))
{
Df_cnt=0;
BUZZ_Count1=0;
BUZZ_Count2++;
Scan_cnt=0;
}
if (ADC1_Collection.Time_Cnt<7)
{
ADC1_Collection.Time_Cnt++;
}
}
void ADC1_COMP_IRQHandler(void)
{
if(ADC_GetFlagStatus(ADC1,ADC_IT_EOC)!=RESET)
{
ADC1_Collection.Sample_Cnt++;
ADC_ClearFlag(ADC1,ADC_IT_EOC);
ADC_Temp = 0xFFF & ADC_GetConversionValue(ADC1);
if ((ADC_Value-ADC_Temp)>=0)
{
ADC_TVul=ADC_Value-ADC_Temp;
}
else if ((ADC_Value-ADC_Temp)<0)
{
ADC_TVul=ADC_Temp-ADC_Value;
}
ADC1_Collection.ADC_DATA_Temp_BUFF[ADC1_Collection.count++]=ADC_TVul;
// if (ADC1_Collection.count>=ADC_N)
// {
// for (unsigned int i=0;i<ADC1_Collection.count;i++)
// {
// ADC1_Collection.sum+=ADC1_Collection.ADC_DATA_Temp_BUFF[i];
// }
// ADC_Result=ADC1_Collection.sum/ADC1_Collection.count;
// ADC1_Collection.sum=0;
// ADC1_Collection.count=0;
// }
ADC1_Collection.Sample_Cnt=0;
}
}
#endif
MMit.h
#ifndef _MM32F0XX_H
#define _MM32F0XX_H
#ifdef __cplusplus
extern "C" {
#endif
#define N 10
void NMI_Handler(void);
void HardFault_Handler(void);
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void ADC1_IRQHandler(void);
void UART1_IRQHandler(void);
void EXTI4_15_IRQHandler(void);
void TIM16_IRQHandler(void);
void TIM16_IRQHandler(void);
#ifdef __cplusplus
}
#endif
#endif /* */
f、main.c
加入了平均值滤波
#include <stdio.h>
#include <string.h>
#include "TIM.h"
#include "GPIOSet.h"
#include "BUZZ.h"
#include "sys.h"
#include "ADC.h"
#include "MM32_it.h"
#include "KEY.h"
int main(void)
{
Systick_Init();
KEY_Init();
SysDelay_ms(1000);
AD1_Configration();
PWM_TIM2_Configuration();
Buzz_init();
KEY_Init();
SysDelay_ms(10);
// ADC_Result=ADC_TVul;
BUZZ1_Control.Buzz_Send_cnt=0;
BUZZ1_Control.BUZZ_Buff[0]=2;
BUZZ1_Control.BUZZ_Status=BUZZ_STON;
Scan1_Flag1_Mode.BUZZ_Flag1=Scan_IDLE;
//shijia ADvalue ADC_Temp
//ADC_Value = mid value
ADC_Value=ADC_Temp;
while(1)
{
Buzz_Control();
KEY_Process();
if((BUZZ_Count1>300))
{
Scan1_Flag1_Mode.BUZZ_Flag1=Scan_yes;
BUZZ1_Control.Buzz_Send_cnt=0;
BUZZ1_Control.BUZZ_Buff[0]=1;
BUZZ1_Control.BUZZ_Status=BUZZ_STON;
BUZZ_Count1=0;
}
else if(BUZZ_Count2>300)//计时
{
Scan1_Flag1_Mode.BUZZ_Flag1=Scan_no;
BUZZ_ON();
BUZZ_Count2=0;
}
if ((ADC1_Collection.count>=ADC_N)||(ADC1_Collection.Time_Cnt>=7))
{
for (unsigned int i=0;i<ADC1_Collection.count;i++)
{
ADC1_Collection.sum+=ADC1_Collection.ADC_DATA_Temp_BUFF[i];
}
ADC_OldResult=ADC1_Collection.sum/ADC1_Collection.count;
ADC_Result=((ADC_OldResult*7)+(ADC_Result*3))/10;
ADC1_Collection.sum=0;
ADC1_Collection.count=0;
ADC1_Collection.Time_Cnt=0;
}
//tianjia 20次取平均值
}
}
三、 基础知识(大部分基于江科大视频)
3.1 C基础
3.1.1 c语言数据类型
3.2 TIM定时器
3.2.1 输出比较 OC
(1)
3.2.2 输入捕获 IC
(1)描述:当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中
(2)作用:可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
3.3 ADC 模数转换
3.3.1 ADC函数理解
(1)GPIO_Mode配置:
GIPO模式配置为GPIO_MODE_AIN模拟输入,此模式下GPIO口无效,即断开GPIO口,防止GPIO的输入输出对模拟电压造成干扰,所以GPIO_MODE_AIN模拟输入为ADC的专属模式
(2)ADC转换四种模式
ADC转换中有两个函数,分别对应四种模式
ADC_ContinuousConvMode();//连续转换模式 可选择ENABLE或DISABLE
ADC_ScanConvMode();//扫描模式 可选择ENABLE或DISABLE
① 非扫描模式下 单次转换模式
此模式下,只有序列1有效
如图,在序列1的位置指定我们想转换的通道(例如通道2),然后触发转换,ADC对通道2进行模式转换,一段时间后转换结束,结果存放在数据寄存器中,同时EOC标志位置1;判断标志位状态可知道转换是否完成,若完成就可以读取寄存器里的数据。
若想再启动一次转换,就要再触发一次。
此模式需要手动开启转换
② 非扫描模式下 连续转换模式
此模式与模式①大体相同,只是该模式转换一次后不会停止,会继续下一轮的转换。
此模式不需要判断转换是否结束,因为一直都在转换 ,所以想要读取AD值时,直接读取。
③ 扫描模式下 单次转换模式 (扫描模式需配合DMA使用)
为了方便理解,可以类比菜单点菜,通道数目可供选择(可重复),触发转换后,ADC对设定的通道(n)进行模数转换,结果放在数据寄存器。
为防止数据被覆盖,需配合DMA及时将数据挪走,n个通道转换完成后,产生EOC信号,转换结束。
若想再启动一次转换,就要再触发一次。
④扫描模式下 连续转换模式 (扫描模式需配合DMA使用)
此模式与模式③大体相同,只是该模式转换一次后不会停止,会继续下一轮的转换。
(3)ADC触发源
(4)STM32ADC T(总转换时间)=T(采样)+T(12.5个ADC周期)
例如:当ADCCLK=14MHz,采样时间为1.5个周期,则T=1.5+12.5=14个周期,即1us
(5)使用定时器触发ADC转换(可实现定时采样)
令T(采样)=15个周期
则T(总转换时间)=15+12=27个周期
此时配置定时器的时钟为30MHz,且将采样频率设置为定时器频率
则T(转换)=27/30MHz=0.9us<1us(一般情况下,采样间隔时间需要大于转换时间,否则有可能AD转化还没完成就被采出来了)
3.3.2 DMA (Direct Memory Acess)直接通道读取
(1)DMA可提供外设与存储器或存储器与存储器之间的高速数据传输,无需CPU的干预,节省CPU资源
(2)存储器映像
3.3 USART串口通信
3.3.1 串口参数
波特率(每秒传输码元的个数,bit/s或bps)、
起始位(固定为低电平)、电平由高电平被拉为
数据位(低位先行)、
校验位、
停止位(固定为高电平)
3.3.2 通信方式
异步通信
所以双方要约定一个速率,ns发送一位,则ns接收一位