基本功能实现

news2024/11/24 12:49:39

目录

1、环境搭建

2、按键控制灯&电机

LED

电机

垂直按键(机械按键)

3、串口调试功能

4、定时器延时和定时器中断

5、振动强弱调节

6、万年历

7、五方向按键

1、原理及分析

2、程序设计


1、环境搭建

需求:

搭建一个STM32F411CEU6工程

分析:

C / C++   宏定义栏: STM32F411xE

在哪里找?   STM32F411xE

2、按键控制灯&电机

需求:

按键控制灯和电机的开关

第一次按键:LED_ON

第二次按键:LED_OFF

第三次按键:MI_ON

第四次按键:MI_OFF

LED

创建.c和.h文件

添加文件到工程

看原理图

        LED所用到的IO口

        低电平点亮 高电平熄灭

        输出模式

程序设计

        LED初始化函数

/***********************************************
*函数名    :led_init
*函数功能  :对LED灯所用IO口初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :LED------PA7
************************************************/
void led_init(void)
{
	RCC->AHB1ENR |= (1<<0);
	GPIOA->MODER &= ~(3<<14);
	GPIOA->MODER |= (1<<14);
	GPIOA->OTYPER &= ~(1<<7);
	GPIOA->OSPEEDR &= ~(3<<14);
	GPIOA->PUPDR &= ~(3<<14);
	
	//初始状态 关
	GPIOA->ODR |= (1<<7);
}


        开关灯的宏定义

#ifndef _LED_H
#define _LED_H
#include "stm32f4xx.h"                  // Device header

#define LED_OFF (GPIOA->ODR |= (1<<7))
#define LED_ON  (GPIOA->ODR &= ~(1<<7))
#define LED_OVERTURN   (GPIOA->ODR ^= (1<<7))

void led_init(void);

#endif



电机

创建.c和.h文件

添加文件到工程

看原理图

        电机所用到的IO口

        高电平转动 低电平停止

        输出模式

程序设计

        电机初始化函数


/***********************************************
*函数名    :motor_init
*函数功能  :对电机所用IO口初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :motor------PB10
************************************************/
void motor_init(void)
{
	RCC->AHB1ENR |= (1<<1);
	GPIOB->MODER &= ~(3<<20);
	GPIOB->MODER |= (1<<20);
	GPIOB->OTYPER &= ~(1<<10);
	GPIOB->OSPEEDR &= ~(3<<20);
	GPIOB->PUPDR &= ~(3<<20);
	
	//初始状态 关
	GPIOB->ODR &= ~(1<<10);
}

        电机转动和停止的宏定义

#ifndef _MOTOR_H
#define _MOTOR_H

#include "stm32f4xx.h"                  // Device header

#define MI_OFF (GPIOB->ODR &= ~(1<<10))
#define MI_ON  (GPIOB->ODR |= (1<<10))
#define MI_OVERTURN   (GPIOB->ODR ^= (1<<10))

void motor_init(void);
#endif

垂直按键(机械按键)

创建.c和.h文件

添加文件到工程

看原理图

        按键所用到的IO口

        高电平按下 低电平抬起

        输入模式

程序设计

        按键初始化函数

static void delay_ms(u32 ms)
{
	u32 i = 100 / 4 * 1000 * ms;
	while(i)
	{
		i--;
	}
}




/***********************************************
*函数名    :key_init
*函数功能  :对KEY所用IO口初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :KEY_OK------PA0
************************************************/
void key_init(void)
{
	RCC->AHB1ENR |= (1<<0);
	GPIOA->MODER &= ~(3<<0);
	GPIOA->PUPDR &= ~(3<<0);
}

        按键扫描函数

/***********************************************
*函数名    :key_scan
*函数功能  :按键扫描函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
************************************************/
u8 key_scan(void)
{
	u8 key = 0xff;
	static u8 key_flag = 1;
	
	//判断按键按下
	if(KEY && key_flag)
	{
		delay_ms(15);
		if(KEY)
		{
			key = KEY_OK;
			key_flag = 0;
			MOTOR_VAL = 1000;
			timer_buff[3]=0;    
			
		}
	}
	
	
	//判断按键抬起
	if(!KEY)
	{
		key_flag = 1;
		
	}
	
	
	return key;
}

3、串口调试功能

程序设计

        串口初始化配置

/***********************************************
*函数名    :usart1_init
*函数功能  :串口1初始化配置
*函数参数  :u32 bps
*函数返回值:无
*函数描述  :USART1_Tx ---------PA9	
            USART1_Rx ---------PA10
************************************************/
void usart1_init(u32 bps)
{
	/*IO口控制器配置*/
	//端口时钟使能
	RCC->AHB1ENR |= (1<<0);
	//端口模式配置
	GPIOA->MODER &= ~((3<<18) | (3<<20));
	GPIOA->MODER |= ((2<<18) | (2<<20));
	//端口输出类型
	GPIOA->OTYPER &= ~(1<<9);
	//端口输出速度
	GPIOA->OSPEEDR &= ~(3<<18);   //2M
	//上下拉配置
	GPIOA->PUPDR &= ~((3<<18) | (3<<20));
	//复用功能配置
	GPIOA->AFR[1] &= ~(15<<4);
	GPIOA->AFR[1] |= (7<<4);     //PA9配置串口1的发送复用功能
	
	GPIOA->AFR[1] &= ~(15<<8);
	GPIOA->AFR[1] |= (7<<8);     //PA10配置串口1的接收复用功能
	
	
	/*串口1控制器配置*/
	//串口时钟使能
	RCC->APB2ENR |= (1<<4);
	//CR1
	USART1->CR1 &= ~(1<<15);       //16倍过采样
	USART1->CR1 &= ~(1<<12);       //8位字长
	USART1->CR1 |= (1<<3)	;        //发送使能
	USART1->CR1 |= (1<<2)	;        //接收使能
	//CR2
	USART1->CR2 &= ~(3<<12);       //1个停止位
	//BRR
	USART1->BRR = 100000000/bps;
	
	
	
	/*NVIC控制器配置*/
	//优先级分组-----------------------------主函数其他初始化上面
	
	//计算优先级编码值
	u32 pri = NVIC_EncodePriority (5,1,2);
	//设确定具体中断源
	NVIC_SetPriority(USART1_IRQn, pri);
	//使能NVIC响应通道
	NVIC_EnableIRQ(USART1_IRQn);
	
	//串口1空闲中断使能
	USART1->CR1 |= (1<<4);
	//串口1接收中断使能
	USART1->CR1 |= (1<<5);
	//串口使能
	USART1->CR1 |= (1<<13);
	
}

        发送一个字节函数

/***********************************************
*函数名    :usart1_send_byte
*函数功能  :串口1发送一个字节函数
*函数参数  :u8 data
*函数返回值:无
*函数描述  :
************************************************/
void usart1_send_byte(u8 data)
{
	//等待之前的发送完成(等待转态寄存器的发送完成位置1)
	while(!(USART1->SR & (1<<6)));

//	//把要发送的数据赋值给数据寄存器
	USART1->DR = data;
	

}

        发送一个字符串函数

/***********************************************
*函数名    :usart1_send_str
*函数功能  :串口1发送一个字符串函数
*函数参数  :u8 *str
*函数返回值:无
*函数描述  :
************************************************/
void usart1_send_str(u8 *str)
{
	while(*str != '\0')
	{
		//发送字符
		usart1_send_byte(*str);
		//下一字符的地址
		str++;
	}
	
}

        中断服务函数

/***********************************************
*函数名    :USART1_IRQHandler
*函数功能  :串口1中断服务函数
*函数参数  :无
*函数返回值:无
*函数描述  :				
************************************************/
u8 usart1_buff[10];

void USART1_IRQHandler(void)
{
	u8 data;
	//判断是接收中断信号触发
	if(USART1->SR & (1<<5))
	{
		//清除中断标志位

		//紧急事件
		
	}
	
	//判断是空闲中断信号触发
	if(USART1->SR & (1<<4))
	{
		//清除中断标志位
		USART1->SR;
		USART1->DR;
		//紧急事件
		
		
	}

	
}

4、定时器延时和定时器中断

定时器延时用timer11

定时器11没有单次计数配置,只能是循环计数,

等待计数完成后,要关闭定时器。

定时器中断用timer9

注意:

时钟频率

分频

不同的芯片可能没有基本定时器6和7

程序设计

定时器11毫秒延时函数

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

}

定时器11微秒延时函数

/***********************************************
*函数名    :TIM11_delay_us
*函数功能  :定时器11延时微秒
*函数参数  :u16 us
*函数返回值:无
*函数描述  :100MHZ--------------100/us
            50分频-----------2/us
************************************************/
void TIM11_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);
}


定时器9毫秒级定时中断函数

/***********************************************
*函数名    :tim9_interrupt_ms
*函数功能  :定时器9毫秒级定时中断
*函数参数  :u16 ms
*函数返回值:无
*函数描述  :100MHZ--------------100000/ms
            10000分频-----------10/ms
************************************************/
void tim9_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 = 10 * ms - 1;
	//EGR    UG位,人为产生更新事件       状态寄存器的0号位置1;
	TIM9->EGR |= (1<<0);
	//清零状态寄存器
	TIM9->SR &= ~(1<<0);	
		
	/*NVIC控制器配置*/	
	//优先级分组
	//计算优先级编码值
	u32 pri = NVIC_EncodePriority (5,1,1);
	//设确定具体中断源
	NVIC_SetPriority(TIM1_BRK_TIM9_IRQn, pri);
	//使能NVIC响应通道
	NVIC_EnableIRQ(TIM1_BRK_TIM9_IRQn);
	
	//DIER  定时中断使能
	TIM9->DIER |= (1<<0);
	//定时器使能
	TIM9->CR1 |= (1<<0);
}

5、振动强弱调节

电机变速用PWM

通过查复用表可知:

电机-----PB10--------TIM2_CH3

程序设计

定时器2通道3输出PWM驱动电机函数

/***********************************************
*函数名    :tim2_ch3_pwm_motor
*函数功能  :定时器2通道3驱动PB10,输出PWM
*函数参数  :无
*函数返回值:无
*函数描述  :100MHZ--------------100000/ms-----100/us
            100分频-----------1/us
            周期定位1ms     PB10
************************************************/
void tim2_ch3_pwm_motor(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] &= ~(15<<8);
	GPIOB->AFR[1] |= (1<<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<<2);
	TIM2->CR1 &= ~(1<<1);
	//SMCR
	TIM2->SMCR &= ~(7<<0);       //选择内部时钟
	//CCMRx
	TIM2->CCMR2 &= ~(3<<0);      //把通道3定位输出通道
	TIM2->CCMR2 |= (1<<3);       //比较寄存器影子
	TIM2->CCMR2 &= ~(7<<4);
	TIM2->CCMR2 |= (6<<4);        //PWM1
	//CCER
	TIM2->CCER &= ~(1<<9);       //高电平为有效电平
	
	//PSC
	TIM2->PSC = 100-1;
	//ARR
	TIM2->ARR = 1000-1;
	//CCRx
	TIM2->CCR3 = 0;
	//EGR
	TIM2->EGR |= (1<<0);         //人为产生更新事件
	
	
	//通道3打开
	TIM2->CCER |= (1<<8);
	//定时器使能
	TIM2->CR1 |= (1<<0);
}

6、万年历

时钟源   外部低速时钟  32.768KHZ

              内部低速时钟  32KHZ

程序设计

RTC初始化配置

/***********************************************
*函数名    :RTC_init
*函数功能  :RTC初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :			
************************************************/
void RTC_init(RTC_t time)
{
	/*解除RTC和相关寄存器保护*/
	//使能电源控制器时钟
	RCC->APB1ENR |= (1<<28);
	//PWR->CR 的DBP位写1解除RTC控制器保护 
	PWR->CR |= (1<<8);
	//秘钥寄存器写入值,解除RTC相关寄存器保护
	RTC->WPR = 0xca;
	RTC->WPR = 0x53;
	
	
	/*RTC时钟源设置*/
	//使能内部低速震荡器
	RCC->CSR |= (1<<0);
	//等待内部部振荡器就绪
	while(!(RCC->CSR & (1<<1)));
	
	//选择内部低速时钟源
	RCC->BDCR |= (2<<8);
	//使能RTC时钟
	RCC->BDCR |= (1<<15);
	
	
	/*RTC相关寄存器*/
	//CR
	RTC->CR &= ~(1<<6);     //24h
	RTC->CR &= ~(1<<5);     //开影子寄存器
	RTC->WPR = 0xff;
	
	
	/*设置初始时间*/
	if(RTC->BKP0R != 500)
	{
		RTC->BKP0R = 500;
		set_time(time);
	}
	

}

设置时间函数

/***********************************************
*函数名    :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)
	while(!(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 & 0xff0000) >> 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 & 0x3f) >> 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;	
}


7、五方向按键

1、原理及分析

需求:

五方向的按键扫描函数

分析:

垂直按键不参与ADC控制

其他四方向的原理:通过ADC转换得到不同的份数,每份份数代表着不同的方向

通过原理图用到的是PA3

通过查表知道用到ADC1的通道3

 

四个按键方向接在ADC1的3号通道上,不同的方向接了不同的电阻,不同的按键方向就会体现不同的电压值,不同电压值通过AD转换,就会体现出不同的份数.不同的份数就可以识别不同方向的按键.

2、程序设计

设计ADC相关程序

ADC的配置方式有两种:中断的方式获取数据、查询等待的方式获取数据

查询等待的方式:

ADC初始化配置函数

/***********************************************
*函数名    :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控制器配置*/
	//ADC控制器时钟使能
	RCC->APB2ENR |= (1<<8);
	//CR1
	ADC1->CR1 &= ~(3<<24);                //精度12位
	ADC1->CR1 |= (1<<8);                  //扫描模式
	//CR2
	ADC1->CR2 &= ~(1<<11);                //右对齐
	ADC1->CR2 |= (1<<10);                 //每个转换完成标志位就置1
	ADC1->CR2 &= ~(1<<1);                 //单次
	//SMPR
	ADC1->SMPR2 |= (7<<9);                 //通道3,480个周期
	//SQR1
	ADC1->SQR1 &= ~(0xf<<20);               //一共有一个转换             
	//SQR3
	ADC1->SQR3 &= ~(0x1f<<0);
	ADC1->SQR3 |= (3<<0);                  //第一个转换是3号通道
	
	//CCR
	ADC->CCR &= ~(3<<16);
	ADC->CCR |= (1<<16);                     //4分频
	
	//ADC使能
	ADC1->CR2 |= (1<<0);
}

 获取转换数据函数


/***********************************************
*函数名    :get_adc1_data
*函数功能  :获取adc1的转换数据
*函数参数  :无
*函数返回值:u16 
*函数描述  :PA3-----ADC1_CH3------四方向按键
************************************************/
u16 get_adc1_data(void)
{
	u16 data;
	
	//开始转换
	ADC1->CR2 |= (1<<30);
	//等待转换完成  
	while(!(ADC1->SR & (1<<1)));
	//获取转换数据到变量
	data = ADC1->DR;
	//返回转换值
	return data;
}

        通过调用ADC的获取函数,再通过测试按键,得到不同方向的份数值

        KEY_UP     1000~1100

        KEY_Down   1300~1400

        KEY_L       >=4000

        KEY_R       2000~2100

设计四方向按键相关程序

        四方向按键扫描函数

/***********************************************
*函数名    :fun_key_init
*函数功能  :五方向按键初始化
*函数参数  :无
*函数返回值:无
*函数描述  :
************************************************/
void fun_key_init(void)
{
	key_init();
	adc1_ch3_Init();
}



/***********************************************
*函数名    :fun_key_scan
*函数功能  :五方向按键扫描函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
************************************************/
u8 fun_key_scan(void)
{
	
	u16 data;
	u8  key_data = 0xff;
	static u8  adc_flag = 1;
	
	
	//四方向按键
	data = get_adc1_data();
	if(adc_flag  &&  data >= 1000 && data <= 1100 )   //UP
	{
		adc_flag = 0;
		key_data = KEY_UP;
	}
	else if(adc_flag  && data >= 1300 && data <= 1400)   //Down
	{
		adc_flag = 0;
		key_data = KEY_Down;
		
	}
	else if(adc_flag  && data > 4000)   //left
	{
		adc_flag = 0;
		key_data = KEY_Left;
	}
	else if(adc_flag  && data >= 2000 && data <= 2100)   //right
	{
		adc_flag = 0;
		key_data = KEY_Right;
	}
	
	
	
	if(data < 20)
	{
		//解锁标志位
		adc_flag = 1;
	}
	
	return key_data;
}

        结合垂直按键写出五方向按键扫描函数

/***********************************************
*函数名    :fun_key_scan
*函数功能  :五方向按键扫描函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
************************************************/
u8 fun_key_scan(void)
{
	
	u16 data;
	u8  key_data = 0xff;
	static u8  adc_flag = 1;
	
	//垂直按键
	key_data = key_scan();
	//四方向按键
	data = get_adc1_data();
	if(adc_flag  &&  data >= 1000 && data <= 1100 )   //UP
	{
		adc_flag = 0;
		key_data = KEY_UP;
	}
	else if(adc_flag  && data >= 1300 && data <= 1400)   //Down
	{
		adc_flag = 0;
		key_data = KEY_Down;
		
	}
	else if(adc_flag  && data > 4000)   //left
	{
		adc_flag = 0;
		key_data = KEY_Left;
	}
	else if(adc_flag  && data >= 2000 && data <= 2100)   //right
	{
		adc_flag = 0;
		key_data = KEY_Right;
	}
	
	if(data < 20)
	{
		//解锁标志位
		adc_flag = 1;
	}
	
	return key_data;
}

若想要实现,按住不抬起能每隔一秒返回一次键值?

利用定时中断,超时检测1S解锁一次标志位

u8 fun_key_scan(void)
{
	
	u16 data;
	u8  key_data = 0xff;
	static u8  adc_flag = 1;
	
	//垂直按键
	key_data = key_scan();
	
	//四方向按键
	data = get_adc1_data();
	if(adc_flag  &&  data >= 1000 && data <= 1100 )   //UP
	{
		adc_flag = 0;
		key_data = KEY_UP;
		timer_buff[3]=0;                  //开始1s计时
		MOTOR_VAL = 1000;
	}
	else if(adc_flag  && data >= 1300 && data <= 1400)   //Down
	{
		adc_flag = 0;
		key_data = KEY_Down;
		timer_buff[3]=0;                 //开始1s计时
		MOTOR_VAL = 1000;
	}
	else if(adc_flag  && data > 4000)   //left
	{
		adc_flag = 0;
		key_data = KEY_Left;
		timer_buff[3]=0;                 //开始1s计时
		MOTOR_VAL = 1000;
	
	}
	else if(adc_flag  && data >= 2000 && data <= 2100)   //right
	{
		adc_flag = 0;
		key_data = KEY_Right;
		timer_buff[3]=0;                 //开始1s计时
		MOTOR_VAL = 1000;
		
	}
	
	
	
	if(data < 20 || timer_buff[3]==1000)
	{
		//解锁标志位
		adc_flag = 1;
		
		
	}
	
	return key_data;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++11新特性探索:Lambda表达式与函数包装器的实用指南

文章目录 前言&#x1f349;一、Lambda表达式&#xff08;匿名函数&#xff09;&#x1f353;1.1 Lambda 表达式的基本语法&#x1f353;1.2 示例&#xff1a;基本 Lambda 表达式&#x1f353;1.3 捕获列表&#xff08;Capture&#xff09;&#x1f353;1.4 使用 Lambda 表达式…

msvcp110.dll丢失修复的多种科学方法分析,详细解析msvcp110.dll文件

遇到“msvcp110.dll丢失”的错误时&#xff0c;这表明你的系统缺少一个关键文件&#xff0c;但解决这一问题比较直接。本文将指导你通过几个简单的步骤迅速修复此错误&#xff0c;确保你的程序或游戏可以顺利运行。接下来的操作将非常简洁明了&#xff0c;易于理解和执行。 一.…

HDR视频技术之四:HDR 主要标准

HDR 是 UHD 技术中最重要维度之一&#xff0c;带来新的视觉呈现体验。 HDR 技术涉及到采集、加工、传输、呈现等视频流程上的多个环节&#xff0c;需要定义出互联互通的产业标准&#xff0c;以支持规模化应用和部署。本文整理当前 HDR 应用中的一些代表性的国际标准。 1 HDR 发…

Bug Fix 20241122:缺少lib文件错误

今天有朋友提醒才突然发现 gitee 上传的代码存在两个很严重&#xff0c;同时也很低级的错误。 因为gitee的默认设置不允许二进制文件的提交&#xff0c; 所以PH47框架下的库文件&#xff08;各逻辑层的库文件&#xff09;&#xff0c;以及Stm32Cube驱动的库文件都没上传到Gi…

c++源码阅读__smart_ptr__正文阅读

文章目录 简介源码解析1. 引用计数的实现方式2. deleter静态方法的赋值时间节点3.make_smart的实现方式 与 好处4. 几种构造函数4.1 空构造函数4.2 接收指针的构造函数4.3 接收指针和删除方法的构造函数 , 以及auto进行模板lambda的编写4.4 拷贝构造函数4.5 赋值运算符 5. rele…

【BUG】ES使用过程中问题解决汇总

安装elasticsearch内存不足问题 问题回顾 运行kibana服务的时候&#xff0c;无法本地访问 解决 首先排查端口问题&#xff0c;然后检查错误日志 无法运行kibana服务&#xff0c;是因为elasticsearch没有启动的原因 发现致命错误&#xff0c;确定是elasticsearch服务没有运行导…

C语言--分支循环编程题目

第一道题目&#xff1a; #include <stdio.h>int main() {//分析&#xff1a;//1.连续读取int a 0;int b 0;int c 0;while (scanf("%d %d %d\n", &a, &b, &c) ! EOF){//2.对三角形的判断//a b c 等边三角形 其中两个相等 等腰三角形 其余情…

Linux——用户级缓存区及模拟实现fopen、fweite、fclose

linux基础io重定向-CSDN博客 文章目录 目录 文章目录 什么是缓冲区 为什么要有缓冲区 二、编写自己的fopen、fwrite、fclose 1.引入函数 2、引入FILE 3.模拟封装 1、fopen 2、fwrite 3、fclose 4、fflush 总结 前言 用快递站讲述缓冲区 收件区&#xff08;类比输…

git(Linux)

1.git 三板斧 基本准备工作&#xff1a; 把远端仓库拉拉取到本地了 .git --> 本地仓库 git在提交的时候&#xff0c;只会提交变化的部分 就可以在当前目录下新增代码了 test.c 并没有被仓库管理起来 怎么添加&#xff1f; 1.1 git add test.c 也不算完全添加到仓库里面&…

GESP2023年9月认证C++四级( 第三部分编程题(1-2))

编程题1&#xff08;string&#xff09;参考程序&#xff1a; #include <iostream> using namespace std; long long hex10(string num,int b) {//int i;long long res0;for(i0;i<num.size();i) if(num[i]>0&&num[i]<9)resres*bnum[i]-0;else //如果nu…

Ultiverse 和web3新玩法?AI和GameFi的结合是怎样

Gamef 和 AI 是我们这个周期十分看好两大赛道之一&#xff0c;(Gamef 拥有极强的破圈效应&#xff0c;引领 Web2 用户进军 Web3 最佳利器。AI是这个周期最热门赛道&#xff0c;无论 Web2的 OpenAl&#xff0c;还是 Web3&#xff0c;都成为话题热议焦点。那么结合 GamefiA1双叙事…

如何在 UniApp 中实现 iOS 版本更新检测

随着移动应用的不断发展&#xff0c;保持应用程序的更新是必不可少的&#xff0c;这样用户才能获得更好的体验。本文将帮助你在 UniApp 中实现 iOS 版的版本更新检测和提示&#xff0c;适合刚入行的小白。我们将分步骤进行说明&#xff0c;每一步所需的代码及其解释都会一一列出…

解决 npm xxx was blocked, reason: xx bad guy, steal env and delete files

问题复现 今天一位朋友说&#xff0c;vue2的老项目安装不老依赖&#xff0c;报错内容如下&#xff1a; npm install 451 Unavailable For Legal Reasons - GET https://registry.npmmirror.com/vab-count - [UNAVAILABLE_FOR_LEGAL_REASONS] vab-count was blocked, reas…

【AI系统】GPU 架构回顾(从2018年-2024年)

Turing 架构 2018 年 Turing 图灵架构发布&#xff0c;采用 TSMC 12 nm 工艺&#xff0c;总共 18.6 亿个晶体管。在 PC 游戏、专业图形应用程序和深度学习推理方面&#xff0c;效率和性能都取得了重大进步。相比上一代 Volta 架构主要更新了 Tensor Core&#xff08;专门为执行…

每天五分钟机器学习:支持向量机数学基础之超平面分离定理

本文重点 超平面分离定理(Separating Hyperplane Theorem)是数学和机器学习领域中的一个重要概念,特别是在凸集理论和最优化理论中有着广泛的应用。该定理表明,在特定的条件下,两个不相交的凸集总可以用一个超平面进行分离。 定义与表述 超平面分离定理(Separating Hy…

docker镜像源配置、换源、dockerhub国内镜像最新可用加速源(仓库)

一、临时拉取方式 在docker pull后先拼接镜像源域名&#xff0c;后面拼接拉取的镜像名 $ docker pull dockerpull.org/continuumio/miniconda3 二、永久配置方式 vim修改/etc/docker/daemon.json&#xff0c;并重启docker服务。 # 创建目录 sudo mkdir -p /etc/docker# 写…

电脑使用——知乎、钉钉组件访问失败解决

最近发现办公电脑知乎、钉钉内置组件访问不了&#xff0c;但同网络下笔记本可以访问&#xff1b;经过检测排除了目标服务异常、防火墙拦截的原因&#xff1b;最后发现是DNS的原因&#xff0c;调整DNS首先项1.1.1.1为114.114.114.114后解决&#xff0c;现插眼记录 首先排除拦截&…

Consumer Group

不&#xff0c;kafka-consumer-groups.sh 脚本本身并不用于创建 Consumer Group。它主要用于管理和查看 Consumer Group 的状态和详情&#xff0c;比如列出所有的 Consumer Group、查看特定 Consumer Group 的详情、删除 Consumer Group 等。 Consumer Group 是由 Kafka 消费者…

pandas与open读取csv/txt文件速度比较

pandas与open读取csv/txt文件速度比较 由于在工作中经常需要读取txt或csv文件&#xff0c;使用pandas与open均可以读取并操作文件内容&#xff0c;但不知道那个速度更快一些&#xff0c;所以写了一个脚本去比较在文件大小不同的情况下读取数据的速度 测试结果: 大小pandas速度…

观察者模式和订阅模式

观察者模式和订阅模式在概念上是相似的&#xff0c;它们都涉及到一个对象&#xff08;通常称为“主题”或“发布者”&#xff09;和多个依赖对象&#xff08;称为“观察者”或“订阅者”&#xff09;之间的关系。然而&#xff0c;尽管它们有相似之处&#xff0c;但在某些方面也…