【嵌入式学习记录_M4项目】智能手环项目_2——基本功能及测试

前  言

        这是我在嵌入式学习的第二阶段——M4理论的学习过程中完成的一个练习,因为内容较多,所以分成几篇文章来发布,如果对您有帮助的话,可以点赞,收藏,关注哦,有其他问题可以私聊交流。

        项目还有需要改进优化的地方,也可以添加其他有趣的功能,期待各位大佬的指点和交流。

        续上篇文章......

目  录

前  言

四、基本功能---2

1.串口

①.c文件

②.h文件

2.中断

①.c文件

②.h文件

3.定时器

①.c文件

②.h文件

4.振动反馈

①.c文件

②.h文件

③使用示例(主函数)

5.万年历(RTC)

①.c文件

②.h文件

③定时获取时间

④使用示例(主函数)

6.五方向按键

①.c文件

②.h文件

③与垂直按键扫描函数结合

④优化---中断

PS:未完待续,请留意博主的下一篇文章


四、基本功能---2

1.串口

①.c文件
#include "stm32f4xx.h"                  // Device header
#include "stdio.h"

/**************************************************************************
* 函数名    : Usart1_Init
* 函数功能  : 对串口1初始化配置
* 函数参数  : u32 bps
* 函数返回值: void
* 函数说明  : 
*							usart1	APB2	100MHZ	
*							PA9  --- USART1_TX
*							PA10 --- USART1_RX
*************************************************************************/
void Usart1_Init(u32 bps)
{
	/*IO配置*/
	//时钟使能
	RCC->AHB1ENR |= (1<<0);
	//端口模式
	GPIOA->MODER &= ~((3<<18) | (3<<20));
	GPIOA->MODER |=  ((2<<18) | (2<<20));
	//上下拉
	GPIOA->PUPDR &= ~((3<<18) | (3<<20));
	//输出类型
	GPIOA->OTYPER &= ~(1<<9);
	//输出速度
	GPIOA->OSPEEDR &= ~(3<<18);
	//复用功能
	GPIOA->AFR[1] &= ~((0xf<<4) | (0xf<<8));
	GPIOA->AFR[1] |=  ((0x7<<4) | (0x7<<8));
	
	/*串口控制器配置*/
	//串口时钟使能
	RCC->APB2ENR |= (1 << 4);
	//BRR
	USART1->BRR = 100000000 / bps;
	//CR1
	USART1->CR1 &= ~(1 << 15);
	USART1->CR1 &= ~(1 << 12);
	USART1->CR1 |= (1 << 3);
	USART1->CR1 |= (1 << 2);
	//CR2
	USART1->CR2 &= ~(3 << 12);
	
	/*NVIC配置*/
	//优先级分组 --- 主函数
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,1,2);
	//确定具体中断源
	NVIC_SetPriority(USART1_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(USART1_IRQn);
	
	//接收中断使能
	USART1->CR1 |= (1 << 5);
	//空闲中断使能
	USART1->CR1 |= (1 << 4);
	//USART1使能
	USART1->CR1 |= (1 << 13);
	
}

/**************************************************************************
* 函数名    :fputc
* 函数功能  :printf底层驱动函数
* 函数参数  :int ch, FILE *f
* 函数返回值:int
* 函数说明  :
*							USART1
*************************************************************************/
#if 1
	#pragma import(__use_no_semihosting)             
	//标准库需要的支持函数                 
	struct __FILE 
	{ 
		int handle; 
		/* Whatever you require here. If the only file you are using is */ 
		/* standard output using printf() for debugging, no file handling */ 
		/* is required. */ 
	}; 
	/* FILE is typedef’ d in stdio.h. */ 
	FILE __stdout;       
	//定义_sys_exit()以避免使用半主机模式    
	int _sys_exit(int x) 
	{ 
		x = x; 
	} 
	//重定义fputc函数 
	int fputc(int ch, FILE *f)
	{      
		while((USART1->SR&(1<<6))==0);//循环发送,直到发送完毕   
		USART1->DR = (u8) ch;      
		return ch;
	}
	#endif
	
/**************************************************************************
* 函数名    :Usart1_Send
* 函数功能  :串口1发送数据函数
* 函数参数  :u8 data
* 函数返回值:void
* 函数说明  :
*							PA9-----USART1_Tx
*							PA10----USART1_Rx
*************************************************************************/
void Usart1_Send(u8 data)
{
	//	等待状态寄存器6号位置1;
	while(!(USART1->SR & (1 << 6)));
	//	将数据赋值给数据寄存器;
	USART1->DR = data;
}

/**************************************************************************
* 函数名    :Usart1_Receive
* 函数功能  :串口1接收数据函数
* 函数参数  :void
* 函数返回值:u8 data
* 函数说明  :
*							PA9  ---- USART1_Tx
*							PA10 ---- USART1_Rx
*************************************************************************/
u8 Usart1_Receive(void)
{
	
	u8 data;
	//	等待状态寄存器5号位置1;
	 while(!(USART1->SR & (1 << 5)));
	//	将数据寄存器的值赋值给变量;
	 data = USART1->DR;
	//	返回变量值;
	return data;
}

/**************************************************************************
* 函数名    :Usart1_Send_Str
* 函数功能  :向串口1发送一个字符串
* 函数参数  :u8 *str
* 函数返回值:void
* 函数说明  :
*************************************************************************/
void Usart1_Send_Str(u8 *str)
{
	while(*str != '\0')
	{
		Usart1_Send(*str);
		str++;
	}
}

/**************************************************************************
* 函数名    :Usart1_Receive_Str
* 函数功能  :向串口1接收一个字符串
* 函数参数  :u8 *str
* 函数返回值:void
* 函数说明  :
*************************************************************************/
void Usart1_Receive_Str(u8 *str)
{
	while(1)
	{
		*str = Usart1_Receive();
		if(*str == '#')
		{
			break;
		}
		str++;
	}
	*str = '\0'; 
	
}

ps:中断要对优先级进行分组,因此在该.c文件初始化配置中配置好NVIC后,要在主函数进行优先级分组,即在主函数调用初始化的函数上面写上如下这条程序。

NVIC_SetPriorityGrouping(5); //优先级分组
②.h文件
#ifndef _UART_H
#define _UART_H



#include "stm32f4xx.h"                  // Device header

//函数声明
void Usart1_Init(u32 bps);
void Usart1_Send(u8 data);
u8 Usart1_Receive(void);
void Usart1_Send_Str(u8 *str);
void Usart1_Receive_Str(u8 *str);

#endif


2.中断

①.c文件

(配置串口中断服务函数并通过串口软件发送字符串控制LED1亮灭)

#include "stm32f4xx.h"
#include "led.h"

/**************************************************************************
* 函数名    :USART1_IRQHandler
* 函数功能  :对串口1中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*							接收中断信号触发
*							空闲中断信号触发
*************************************************************************/
u8 usart1_buff[30];
u8 usart1_flag = 0;
void USART1_IRQHandler(void)
{
	static u8 i = 0;
	//判断接收中断信号触发
	if(USART1->SR & (1 << 5))
	{
		//清除中断标志位
		//紧急事件
		usart1_flag = 0;
		usart1_buff[i] = USART1->DR;
		i++;
	}
	//判断空闲中断信号触发
	if(USART1->SR & (1 << 4))
	{
		//清除中断标志位
		USART1->SR;
		USART1->DR;
		//紧急事件
		usart1_buff[i] = '\0';
		i=0;
		usart1_flag = 1;
		//接收回来的数据与字符串进行比较
		if(strcmp((char*)usart1_buff,"open")==0)
		{
			LED1_ON;
		}
		if(strcmp((char*)usart1_buff,"close")==0)
		{
			LED1_OFF;
		}
	}
}
②.h文件
#ifndef _NVIC_H
#define _NVIC_H



#include "stm32f4xx.h"                  // Device header

//声明外部变量
extern u8 usart1_buff[30];
extern u8 usart1_flag;

#endif


3.定时器

延时:TIM11                定时中断:TIM9

①.c文件
#include "stm32f4xx.h"                  // Device header

/**************************************************************************
* 函数名    :Timer11_Delay_Ms
* 函数功能  :通用定时器11毫秒级延时函数
* 函数参数  :u16 ms
* 函数返回值:void
* 函数说明  :100M 	10000分频		10/ms 	
*************************************************************************/
void Timer11_Delay_Ms(u16 ms)
{
	//时钟使能
	RCC->APB2ENR |= (1 << 18);
	//CR1
	TIM11->CR1 &= ~(3 << 8);
	TIM11->CR1 |= (1 << 7);					//打开自动重载预装载使能
	TIM11->CR1 &= ~(1 << 1);					//产生更新事件的条件
	//PSC
	TIM11->PSC = 10000 - 1;						//分频
	//ARR
	TIM11->ARR = ms * 10  -1;					//重载值
	//人为产生更新事件UG
	TIM11->EGR |= (1 << 0);					//生成寄存器更新事件
	//清除计数完成标志位
	TIM11->SR &= ~(1 << 0);	
	
	//使能计数器
	TIM11->CR1 |= (1 << 0);
	//等待计数完成
	while(!(TIM11->SR & (1 << 0)));	//计数完成后TIM11->SR0号位会置1
	//关闭计数器
	TIM11->CR1 &= ~(1 << 0);
}

/***********************************************
*函数名    :TIM11_delay_us
*函数功能  :定时器11延时微秒
*函数参数  :u16 us
*函数返回值:无
*函数描述  :100MHZ--------------100/us
            50分频-----------2/us
************************************************/
void Timer11_Delay_Us(u16 us)
{
	//定时器时钟使能
	RCC->APB2ENR |= (1<<18);
	//CR1
	TIM11->CR1 |= (1<<7);          //使能影子寄存器
	//TIM11->CR1 |= (1<<3);          //单次计数模式
	TIM11->CR1 &= ~(1<<1);         //产生更新事件
	
	//PSC分频寄存器
	TIM11->PSC = 50-1;           //50分频
	//ARR重载寄存器
	TIM11->ARR = 2 * us - 1;
	//人为产生更新事件UG
	TIM11->EGR |= (1<<0);
	//清除状态寄存器更新完成位
	TIM11->SR &= ~(1<<0);
	//使能计数器
	TIM11->CR1 |= (1<<0);
	//等待计数完成
	while(!(TIM11->SR & (1<<0)));
	
	//关闭定时器
	TIM11->CR1 &= ~(1<<0);
}




/***********************************************
*函数名    :Timer9_interrupt_ms
*函数功能  :基本定时器9毫秒级定时中断
*函数参数  :u16 ms
*函数返回值:无
*函数描述  :100M  10000分频		10/ms   Max:6553ms
************************************************/
void Timer9_interrupt_ms(u16 ms)
{
	/*定时器控制器配置*/
	//时钟使能
	RCC->APB2ENR |= (1 << 16);
	//CR1
	TIM9->CR1 |= (1 << 7);					//影子寄存器 
	TIM9->CR1 &= ~(1 << 3);					//连续计数
	TIM9->CR1 &= ~(1 << 2);					//产生中断条件
	TIM9->CR1 &= ~(1 << 1);					//产生更新事件条件
	//PSC
	TIM9->PSC = 10000 - 1;						//分频
	//ARR
	TIM9->ARR = ms * 10  -1;					//重载值
	//人为产生更新事件UG
	TIM9->EGR |= (1 << 0);					//生成寄存器更新事件
	//清除计数完成标志位
	TIM9->SR &= ~(1 << 0);
	
	//优先级编码值
	u32 pri = NVIC_EncodePriority (5,2,3);
	//确定具体中断源
	NVIC_SetPriority(TIM1_BRK_TIM9_IRQn,pri);
	//使能NAIC响应通道
	NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
	
	//中断使能
	TIM9->DIER |= (1 << 0);
	//使能计数器
	TIM9->CR1 |= (1 << 0);
}
②.h文件
#ifndef _TIMER_H
#define _TIMER_H



#include "stm32f4xx.h"                  // Device header

//函数声明
void Timer11_Delay_Ms(u16 ms);
void Timer11_Delay_Us(u16 us);
void Timer9_interrupt_ms(u16 ms);


#endif


③TIM9中断服务函数

(放到中断的.c文件中,测试:定时2s关闭电机)

/**************************************************************************
* 函数名    :TIM9_IRQHandler
* 函数功能  :基本定时器TIM9中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
u32 timer9_count[10];
void TIM1_BRK_TIM9_IRQHandler(void)
{
	//清除标志位
	TIM9->SR &= ~(1 << 0);
	//紧急事件
	timer9_count[0]++;

	if(timer9_count[0] == 2000)
	{
		timer9_count[0] = 0;
		MI_OFF;
	}
}

4.振动反馈

(振动反馈用的是TIM2的通道3的输出比较功能,改变PWM控制电机的转速从而达到振动效果,电机的IO口模式应该用复用功能,故要不能用之前的电机初始化函数)

①.c文件
/***********************************************
*函数名    :motor_pwm
*函数功能  :定时器2通道3输出PWM控制电机速度
*函数参数  :无
*函数返回值:无
*函数描述  :50M  100分频		1/us  T:1ms
*						电机-----PB10--------TIM2_CH3
************************************************/
void Motor_Pwm_Init(void)
{
	Tim2_ch3_pwm_Init();
}


/***********************************************
*函数名    :Tim2_ch3_pwm_Init
*函数功能  :定时器2通道3输出PWM控制电机速度
*函数参数  :无
*函数返回值:无
*函数描述  :50M  100分频		1/us  T:1ms
*						电机-----PB10--------TIM2_CH3
************************************************/
void Tim2_ch3_pwm_Init(void)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1 << 1);
	//端口模式寄存器 --- 复用
	GPIOB->MODER &= ~(3 << 20);
	GPIOB->MODER |= (2 << 20);
	//端口输出类型寄存器
	GPIOB->OTYPER &= ~(1 << 10);
	//端口输出速度寄存器
	GPIOB->OSPEEDR &= ~(3 << 20);
	//端口上拉下拉寄存器
	GPIOB->PUPDR &= ~(3 << 20);
	//端口复用功能寄存器
	GPIOB->AFR[1] &= ~(0xf << 8);
	GPIOB->AFR[1] |= (0x1 << 8);
	
	/*定时器对应通道配置*/
	// 定时器时钟使能
	RCC->APB1ENR |= (1 << 0);
	// CR1
	TIM2->CR1 |= (1 << 7);						//打开重载影子寄存器
	TIM2->CR1 &= ~(3 << 5);						//配置边沿对齐模式
	TIM2->CR1 &= ~(1 << 4);						//计数器递增计数
	TIM2->CR1 &= ~(1 << 3);						//计数器在发生更新事件时不会停止计数
	TIM2->CR1 &= ~(1 << 1);						//使能更新事件
	// SMRC
	TIM2->SMCR &= ~(7 << 0);					//选择内部时钟
	// CCMRx
	TIM2->CCMR2 &= ~(3 << 0);					//CC3 通道配置为输出
	TIM2->CCMR2 |= (1 << 3);					//影子寄存器使能
	TIM2->CCMR2 &= ~(7 << 4);					//PWM 模式 1
	TIM2->CCMR2 |= (6 << 4);
	// CCER
	TIM2->CCER |= (1 << 8);						//开启––在相应输出引脚上输出 OC3 信号
	TIM2->CCER &= ~(1 << 9);						//OC3 高电平有效
	// PSC
	TIM2->PSC = 100 - 1;  
	// ARR
	TIM2->ARR = 1000 - 1;
	// CCRx
	TIM2->CCR3 = 0;
	// EGR
	TIM2->EGR |= (1 << 0);						//重新初始化计数器并生成寄存器更新事件
	//计数器使能
	TIM2->CR1 |= (1 << 0);
}


②.h文件
#ifndef _FUNCTION_H
#define _FUNCTION_H



#include "stm32f4xx.h"                  // Device header

//函数声明
void Motor_Pwm_Init(void);
void Tim2_ch3_pwm_Init(void);
#endif


③使用示例(主函数)
#include "stm32f4xx.h"                  // Device header
#include "led.h"
#include "motor.h"
#include "key.h"
#include "uart.h"
#include "stdio.h"
#include "timer.h"
#include "function.h"

int main(void)
{
	u8 key;
	NVIC_SetPriorityGrouping(5); //优先级分组
	Usart1_Init(115200);
	Led_Init();
//	Motor_Init();
	Key_Init();
	Motor_Pwm_Init();
	Timer9_interrupt_ms(1);
	
	while(1)
	{
        //轮询按键扫描函数
		key = Key_Scanf();
		if(key==1)
		{
            //改变PWM占空比
			TIM2->CCR3 += 100;
			if(TIM2->CCR3==1100)
			{
				TIM2->CCR3 = 0;
			}
		}
	}
	
	return 0;
}

5.万年历(RTC)

(因为文档板子的外部低速时钟坏了,所以用内部低速时钟)

①.c文件
#include "stm32f4xx.h"
#include "rtc.h"
/**************************************************************************
* 函数名    : RTC_Init
* 函数功能  :对RTC初始化配置
* 函数参数  :RTC_t time
* 函数返回值:void
* 函数说明  :
*************************************************************************/
void RTC_Init(RTC_t time)
{
	/*解锁RTC和相关寄存器保护*/
	//使能电源时钟
	RCC->APB1ENR |= (1<<28);
	//使能RTC
	PWR->CR |= (1<<8);
	
	/*RTC时钟源设置*/
	//内部低速振荡器使能
	RCC->CSR |= (1<<0);
	//等待内部振荡器就绪
	while(!(RCC->CSR & (1<<1)));
	//选择内部低速时钟源
	RCC->BDCR |= (2<<8);
	//使能RTC时钟
	RCC->BDCR |= (1<<15);
	
	/*解除RTC保护*/
	RTC->WPR = 0xca;
	RTC->WPR = 0x53;
	
	/*RTC寄存器配置*/
	//CR
	RTC->CR &= ~(1<<6);     //24h
	RTC->CR &= ~(1<<5);     //开影子寄存器
	RTC->WPR = 0xff;				//激活写保护
	
	/*设置初始时间*/
	if(RTC->BKP0R!=500)
	{
		set_time(time);
		RTC->BKP0R=500;
	}
}


/***********************************************
*函数名    :in_dec_out_bcd
*函数功能  :将十进制数据转换成BCD码形式
*函数参数  :u8 dec
*函数返回值:u8
*函数描述  :设置时间时候使用			
************************************************/
u8 in_dec_out_bcd(u8 dec)
{
	return  ((dec / 10) << 4) | (dec % 10);
}




/***********************************************
*函数名    :set_time
*函数功能  :设置时间函数
*函数参数  :RTC_t time
*函数返回值:无
*函数描述  :			
************************************************/
void set_time(RTC_t time)
{
	u32 temp_t = 0;
	u32 temp_d = 0;
	
	//解除寄存器保护
	RTC->WPR = 0xca;
	RTC->WPR = 0x53;
	//让日历进入初始化模式
	RTC->ISR |= (1<<7);
	//等待可以更新日历值
	while(!(RTC->ISR & (1<<6)));
	//将设置的十进制数据转换成BCD码
	temp_t = (in_dec_out_bcd(time.h))<<16  |
	         (in_dec_out_bcd(time.m))<<8   |
	         in_dec_out_bcd(time.s);
	  
	temp_d =(in_dec_out_bcd(time.year-2000))<<16 |
					(in_dec_out_bcd(time.week))<<13 |
					(in_dec_out_bcd(time.mon))<<8   |
					(in_dec_out_bcd(time.day));
	
	//设置TR 和 DR
	RTC->TR = temp_t;
	RTC->DR = temp_d;
	//退出初始化模式
	RTC->ISR &= ~(1<<7);
	//激活写保护
	RTC->WPR = 0xff;

}

/***********************************************
*函数名    :in_bcd_out_dec
*函数功能  :将BCD码形式数据转换成十进制
*函数参数  :u8 bcd
*函数返回值:u8
*函数描述  :获取时间使用	
************************************************/
u8 in_bcd_out_dec(u8 bcd)
{
	return  (bcd  >> 4) * 10 + (bcd & 0x0f);
}


/***********************************************
*函数名    :get_time
*函数功能  :获取时间函数
*函数参数  :无
*函数返回值:RTC_t
*函数描述  :			
************************************************/
RTC_t get_time(void)
{
	RTC_t t;
	u32 temp_t = 0;
	u32 temp_d = 0;
	/*解除控制器保护*/
	RTC->WPR = 0xca;
	RTC->WPR = 0x53;
	
	/*获取时间和日期*/
	//将ISR寄存器中的RSF位置0
	RTC->ISR &= ~(1<<5);
	//等待同步完成(等待ISR寄存器中RSF位自动变为1)
	RTC->ISR |= (1<<5);
	//读取寄存器中的时间(BCD码)
	temp_t = RTC->TR;
	
	//将ISR寄存器中的RSF位置0
	RTC->ISR &= ~(1<<5);
	//等待同步完成(等待ISR寄存器中RSF位自动变为1)
		while(!(RTC->ISR & (1<<5)));
	//读取寄存器中的日期(BCD码)
	temp_d = RTC->DR;
	
	//将读出的BCD码转换成十进制形式
	t.year = in_bcd_out_dec(temp_d>>16)+2000;
	t.week = in_bcd_out_dec((temp_d & 0xe000)>>13);
	t.mon  = in_bcd_out_dec((temp_d & 0x1f00)>>8);
	t.day  = in_bcd_out_dec(temp_d>>0);
	
	t.h = in_bcd_out_dec(temp_t>>16);
	t.m = in_bcd_out_dec(temp_t>>8);
	t.s = in_bcd_out_dec(temp_t>>0);
	
	/*激活写保护*/
	RTC->WPR = 0xff;
	
	return t;
}
②.h文件
#ifndef _RTC_H
#define _RTC_H

#include "stm32f4xx.h"

//定义结构体
typedef struct rtc
{
	//年月日
	u16 year;
	u8  mon;
	u8  day;
	//星期
	u8  week;
	//时分秒
	u8  h;
	u8  m;
	u8  s;
}RTC_t;

//函数声明
void RTC_Init(RTC_t time);
u8 in_dec_out_bcd(u8 dec);
void set_time(RTC_t time);
u8 in_bcd_out_dec(u8 bcd);
RTC_t get_time(void);

#endif


③定时获取时间

(通过TIM9的定时中断,1s获取一次时间,并通过串口打印检测)

/**************************************************************************
* 函数名    :TIM9_IRQHandler
* 函数功能  :基本定时器TIM9中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
u32 timer9_count[10];
RTC_t tim;
void TIM1_BRK_TIM9_IRQHandler(void)
{
	//清除标志位
	TIM9->SR &= ~(1 << 0);
	//紧急事件
	timer9_count[0]++;
	timer9_count[1]++;

	if(timer9_count[0] == 2000)
	{
		timer9_count[0] = 0;
		MI_OFF;
	}
	
	if(timer9_count[1] == 1000)
	{
		timer9_count[1] = 0;
		tim = get_time();
		printf("%d年%d月%d日  星期%d  %02d:%02d:%02d\r\n",tim.year,tim.mon,tim.day,tim.week,tim.h,tim.m,tim.s);
	}
}
④使用示例(主函数)
#include "stm32f4xx.h"                  // Device header
#include "led.h"
#include "motor.h"
#include "key.h"
#include "uart.h"
#include "stdio.h"
#include "timer.h"
#include "function.h"
#include "rtc.h"

int main(void)
{
	RTC_t t = {2024,4,8,1,15,9,30};
	
	NVIC_SetPriorityGrouping(5); //优先级分组
	Usart1_Init(115200);

	Timer9_interrupt_ms(1);
	RTC_Init(t);
	
	while(1)
	{
		
	}
	
	return 0;
}

6.五方向按键

(我板子上的按键是垂直按键加四方向按键,垂直按键前面写过了,四方向接在ADC1的3号通道上,不同的方向接了不同的电阻,不同的按键方向就会体现不同的电压值,不同电压值通过AD转换,就会体现出不同的份数不同的份数就可以识别不同方向的按键。

KEY_UP     1000~1100        KEY_Down   1300~1400

KEY_L       >=4000                KEY_R       2000~2100 

)

①.c文件
#include "stm32f4xx.h" 

/***********************************************
*函数名    :adc1_ch3_init
*函数功能  :ADC1通道3初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :PA3 --- ADC1_CH3 --- 五方向按键		
************************************************/
void adc1_ch3_init(void)
{
	/*io控制器配置*/
	//时钟使能
	RCC->AHB1ENR |= (1 << 0);
	GPIOA->MODER &= ~(3 << 6);//模拟模式
	GPIOA->MODER |= (3 << 6);
	
	/*ADC控制器配置*/
	//时钟使能
	RCC->APB2ENR |= (1 << 8);
	//CR1
	ADC1->CR1 &= ~(3 << 24);//分辨率:12位 --- 4096份
	ADC1->CR1 &= ~(1 << 8);//单通道 禁止扫描
	
	//CR2
	//CR2-SWSTART
	ADC1->CR2 &= ~(1 << 11);//右对齐
	ADC1->CR2 |= (1 << 10);//在每个规则转换结束时将 EOC 位置 1
	ADC1->CR2 &= ~(1 << 1);//单次转换
	
	//SMPR1
	ADC1->SMPR2 &= ~(7 << 9);
	ADC1->SMPR2 |=  (7 << 9);//通道3采样时间480个周期
	
	//SQR
	ADC1->SQR1 &= ~(0xf << 20);//1次转换
	
	ADC1->SQR3 &= ~(0x1f << 0);
	ADC1->SQR3 |= (3 << 0);//ADC规则序列
	
	//CCR
	ADC->CCR &= ~(3 << 16);//2分频
	ADC->CCR |=  (1 << 16);
	
	//使能ADC
	ADC1->CR2 |= (1 << 0);
}

/***********************************************
*函数名    :get_adc1_ch3_data
*函数功能  :获取ADC1的通道3的转换数据
*函数参数  :无
*函数返回值:u16
*函数描述  :
************************************************/
u16 get_adc1_ch3_data(void)
{
	u16 data;
	ADC1->CR2 |= (1 << 30);//开始转换
	while(!(ADC1->SR & (1 << 1)));
	data = ADC1->DR;
	return data;
}

②.h文件
#ifndef _ADC_H
#define _ADC_H

#include "stm32f4xx.h"

//函数声明
void adc1_ch3_init(void);
u16 get_adc1_ch3_data(void);
#endif
③与垂直按键扫描函数结合
/***********************************************
*函数名    :fun_key_scan
*函数功能  :五方向按键扫描函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
************************************************/
u8 fun_key_scan(void)
{
	u16 data;
	u8  key_data = 0xff;
	static u8  adc_flag = 1;
	
	//垂直按键
	key_data = Key_Scanf();
	
	//四方向按键
	data = get_adc1_ch3_data();
	if(adc_flag  &&  data >= 1000 && data <= 1100 )   //UP
	{
		adc_flag = 0;
		key_data = KEY_UP;
		timer9_count[3]=0;                  //开始1s计时
	}
	else if(adc_flag  && data >= 1300 && data <= 1400)   //Down
	{
		adc_flag = 0;
		key_data = KEY_Down;
		timer9_count[3]=0;                 //开始1s计时
	}
	else if(adc_flag  && data > 4000)   //left
	{
		adc_flag = 0;
		key_data = KEY_Left;
		timer9_count[3]=0;                 //开始1s计时
	}
	else if(adc_flag  && data >= 2000 && data <= 2100)   //right
	{
		adc_flag = 0;
		key_data = KEY_Right;
		timer9_count[3]=0;                 //开始1s计时
	}
	
	if(data < 20 || timer9_count[3]==1000)
	{
		//解锁标志位
		adc_flag = 1;
		timer9_count[3]=0;
	}
	
	return key_data;
}

④优化---中断

(按键识别1s后,如果还没有抬起按键,要再次识别一次---定时中断)

/**************************************************************************
* 函数名    :TIM9_IRQHandler
* 函数功能  :基本定时器TIM9中断服务函数配置
* 函数参数  :void
* 函数返回值:void
* 函数说明  :
*************************************************************************/
u32 timer9_count[10];
RTC_t tim;
void TIM1_BRK_TIM9_IRQHandler(void)
{
	//清除标志位
	TIM9->SR &= ~(1 << 0);
	//紧急事件
	//timer9_count[0]++;
	//timer9_count[1]++;
	//timer9_count[2]++;
	timer9_count[3]++;

	if(timer9_count[0] == 2000)
	{
		timer9_count[0] = 0;
		MI_OFF;
	}
	
	if(timer9_count[1] == 1000)
	{
		timer9_count[1] = 0;
		tim = get_time();
		printf("%d年%d月%d日  星期%d  %02d:%02d:%02d\r\n",tim.year,tim.mon,tim.day,tim.week,tim.h,tim.m,tim.s);
	}
	
	//定时转换ADC数据数据
	if(timer9_count[2]==100)
	{
		timer9_count[2] = 0;
		ADC1->CR2 |= (1<<30);//开始转换
	}
	
	//按键识别1s后,如果还没有抬起按键,要再次识别一次
	if(timer9_count[3]==1000)
	{
		timer9_count[3] = 0;
		
	}
}

PS:未完待续,请留意博主的下一篇文章

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值