前 言
这是我在嵌入式学习的第二阶段——M4理论的学习过程中完成的一个练习,因为内容较多,所以分成几篇文章来发布,如果对您有帮助的话,可以点赞,收藏,关注哦,有其他问题可以私聊交流。
项目还有需要改进优化的地方,也可以添加其他有趣的功能,期待各位大佬的指点和交流。
续上篇文章......
目 录
四、基本功能---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;
}
}