本帖最后由 lemonhub 于 2024-5-6 20:14 编辑
极海APM32F411V开发板评测 按键、串口、定时器简测
串口简测从数据手册可以看出,APM32F411共有6个USART串口。
结合官方例程和STM32的经验,对APM32进行串口接收中断初始化。 #include "main.h"
#include "Bsp_Usart.h"
#define DATA_BUF_SIZE (32)
/** USART1 receive buffer*/
uint8_t rxDataBufUSART1[DATA_BUF_SIZE] = {0};
uint32_t rxCountUSART1 = 0;
/*!
* <a href="home.php?mod=space&uid=247401" target="_blank">@brief</a> Delay
*
* @param count: delay count
*
* @retval None
*/
void Delay(uint32_t count)
{
volatile uint32_t delay = count;
while(delay--);
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] USARTS Initialization
*
* @param None
*
* @retval None
*/
void USART1_Init(void)
{
GPIO_Config_T GPIO_configStruct;
GPIO_ConfigStructInit(&GPIO_configStruct);
USART_Config_T usartConfigStruct;
/*使能GPIO时钟*/
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
/*连接PA9至USART1_TX*/
GPIO_ConfigPinAF(GPIOA,GPIO_PIN_SOURCE_9, GPIO_AF_USART1);
/* 配置PA9 USART1_TX 推拉模式 */
GPIO_configStruct.mode = GPIO_MODE_AF;
GPIO_configStruct.pin = GPIO_PIN_9;
GPIO_configStruct.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &GPIO_configStruct);
/*连接PA10至USART1_RX*/
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_10,GPIO_AF_USART1);
/* 配置PA10 USART1_RX 推拉模式 */
GPIO_configStruct.mode = GPIO_MODE_AF;
GPIO_configStruct.pin = GPIO_PIN_10;
GPIO_Config(GPIOA, &GPIO_configStruct);
/*使能USART1时钟*/
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1);
usartConfigStruct.baudRate = 115200;
usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
usartConfigStruct.mode = USART_MODE_TX_RX;
usartConfigStruct.parity = USART_PARITY_NONE;
usartConfigStruct.stopBits = USART_STOP_BIT_1;
usartConfigStruct.wordLength = USART_WORD_LEN_8B;
USART_Config(USART1, &usartConfigStruct);
/* 使能USART1 */
USART_Enable(USART1);
Delay(0x7FFF);//例程中进行了延时
/* 配置USART1中断 */
USART_EnableInterrupt(USART1, USART_INT_RXBNE);//接收中断
USART_ClearStatusFlag(USART1, USART_FLAG_RXBNE);//清楚中断标志位
NVIC_EnableIRQRequest(USART1_IRQn,1,0);
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] This function handles USART1 RX interrupt Handler
*
* @param None
*
* @retval None
*
* <a href="home.php?mod=space&uid=536309" target="_blank">@NOTE</a> This function need to put into void USART1_IRQHandler(void)
*/
void USART_Receive_Isr(void)
{
/* USART1 Recieve Data */
if(USART_ReadStatusFlag(USART1, USART_FLAG_RXBNE) == SET)
{
rxDataBufUSART1[rxCountUSART1++] = (uint8_t)USART_RxData(USART1);
}
}
void USART_Write(USART_T* usart,uint8_t *dat,uint32_t count)
{
while(count--)
{
while(USART_ReadStatusFlag(usart, USART_FLAG_TXBE) == RESET);//在接收空闲时,再发送
USART_TxData(usart, *dat++);
//Delay(0x5FFFF);//例程中有,但去掉后,少量发送暂时未发现影响
}
}
/*!
* @brief This function handles USART1 RX interrupt Handler
*
* @param None
*
* @retval None
*
* @note
*/
void USART1_IRQHandler(void)
{
USART_Receive_Isr();
}
按键检测按键代码直接迁移了例程部分,通过原理图可看出,当按下时,引脚状态为0。
#include "main.h"
#include "Bsp_Key.h"
#define BUTTONn 2
/**
* @brief Key1 push-button
*/
#define KEY1_BUTTON_PIN GPIO_PIN_1
#define KEY1_BUTTON_GPIO_PORT GPIOA
#define KEY1_BUTTON_GPIO_CLK RCM_AHB1_PERIPH_GPIOA
#define KEY1_BUTTON_EINT_LINE EINT_LINE_1
#define KEY1_BUTTON_EINT_PORT_SOURCE SYSCFG_PORT_GPIOA
#define KEY1_BUTTON_EINT_PIN_SOURCE SYSCFG_PIN_1
#define KEY1_BUTTON_EINT_IRQn EINT1_IRQn
/**
* @brief Key2 push-button
*/
#define KEY2_BUTTON_PIN GPIO_PIN_0
#define KEY2_BUTTON_GPIO_PORT GPIOA
#define KEY2_BUTTON_GPIO_CLK RCM_AHB1_PERIPH_GPIOA
#define KEY2_BUTTON_EINT_LINE EINT_LINE_0
#define KEY2_BUTTON_EINT_PORT_SOURCE SYSCFG_PORT_GPIOA
#define KEY2_BUTTON_EINT_PIN_SOURCE SYSCFG_PIN_0
#define KEY2_BUTTON_EINT_IRQn EINT0_IRQn
#define BUTTONn 2
GPIO_T* BUTTON_PORT[BUTTONn] = {KEY1_BUTTON_GPIO_PORT, KEY2_BUTTON_GPIO_PORT};
const uint16_t BUTTON_PIN[BUTTONn] = {KEY1_BUTTON_PIN, KEY2_BUTTON_PIN};
const uint32_t BUTTON_CLK[BUTTONn] = {KEY1_BUTTON_GPIO_CLK, KEY2_BUTTON_GPIO_CLK};
const EINT_LINE_T BUTTON_EINT_LINE[BUTTONn] = {KEY1_BUTTON_EINT_LINE, KEY2_BUTTON_EINT_LINE};
const SYSCFG_PORT_T BUTTON_PORT_SOURCE[BUTTONn] = {KEY1_BUTTON_EINT_PORT_SOURCE, KEY2_BUTTON_EINT_PORT_SOURCE};
const SYSCFG_PIN_T BUTTON_PIN_SOURCE[BUTTONn] = {KEY1_BUTTON_EINT_PIN_SOURCE, KEY2_BUTTON_EINT_PIN_SOURCE};
const IRQn_Type BUTTON_IRQn[BUTTONn] = {KEY1_BUTTON_EINT_IRQn, KEY2_BUTTON_EINT_IRQn};
/*!
* @brief Configures Button GPIO and EINT Line.
*
* @param Button: Specifies the Button to be configured.
* This parameter can be one of following parameters:
* <a href="home.php?mod=space&uid=2817080" target="_blank">@ARG</a> BUTTON_KEY1: Key1 Push Button
* [url=home.php?mod=space&uid=2817080]@ARG[/url] BUTTON_KEY2: Key2 Push Button
* @param Button_Mode: Specifies Button mode.
* This parameter can be one of following parameters:
* [url=home.php?mod=space&uid=2817080]@ARG[/url] BUTTON_MODE_GPIO: Button will be used as simple IO
* @arg BUTTON_MODE_EINT: Button will be connected to EINT line
* with interrupt generation capability
*
* @retval None
*/
void APM_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode)
{
GPIO_Config_T GPIO_configStruct;
EINT_Config_T EINT_configStruct;
/* Enable the BUTTON Clock */
RCM_EnableAHB1PeriphClock(BUTTON_CLK[Button]);
/* Configure Button pin as input floating */
GPIO_ConfigStructInit(&GPIO_configStruct);
GPIO_configStruct.mode = GPIO_MODE_IN;
GPIO_configStruct.pin = BUTTON_PIN[Button];
GPIO_configStruct.pupd = GPIO_PUPD_UP;
GPIO_Config(BUTTON_PORT[Button], &GPIO_configStruct);
if (Button_Mode == BUTTON_MODE_EINT)
{
/* Enable the SYSCFG Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
/* Connect Button EINT Line to Button GPIO Pin */
SYSCFG_ConfigEINTLine(BUTTON_PORT_SOURCE[Button], BUTTON_PIN_SOURCE[Button]);
/* Configure Button EINT line */
EINT_configStruct.line = BUTTON_EINT_LINE[Button];
EINT_configStruct.mode = EINT_MODE_INTERRUPT;
EINT_configStruct.trigger = EINT_TRIGGER_FALLING;
EINT_configStruct.lineCmd = ENABLE;
EINT_Config(&EINT_configStruct);
/* Enable and set Button EINT Interrupt to the lowest priority */
NVIC_EnableIRQRequest(BUTTON_IRQn[Button], 0x0f, 0x0f);
}
}
/*!
* @brief Returns the selected Button state.
*
* @param Button: Specifies the Button to be configured.
* This parameter can be one of following parameters:
* @arg BUTTON_KEY1: Key1 Push Button
* @arg BUTTON_KEY2: Key2 Push Button
*
* @retval The Button GPIO pin value.
*/
uint32_t APM_PBGetState(Button_TypeDef Button)
{
return GPIO_ReadInputBit(BUTTON_PORT[Button], BUTTON_PIN[Button]);
}
/*!
* @brief This function handles External line 0 Handler
*
* @param None
*
* @retval None
*
*/
void EINT0_IRQHandler(void)
{
if(EINT_ReadIntFlag(EINT_LINE_0))
{
APM_LEDToggle(LED2);
/*Clear EINT_LINE0 interrupt flag*/
EINT_ClearIntFlag(EINT_LINE_0);
}
}
/*!
* @brief This function handles External lines 1 Handler
*
* @param None
*
* @retval None
*
*/
void EINT1_IRQHandler(void)
{
if(EINT_ReadIntFlag(EINT_LINE_1))
{
APM_LEDToggle(LED3);
/*Clear EINT_LINE0 interrupt flag*/
EINT_ClearIntFlag(EINT_LINE_1);
}
}MultiButton按键检测MultiButton开源框架仓库 https://github.com/0x1abin/MultiButton 参考博客https://blog.csdn.net/qq_36075612/article/details/115901032 MultiButton | 一个小巧简单易用的事件驱动型按键驱动模块 https://zhuanlan.zhihu.com/p/128961191 本次使用的是博客中的版本,仓库版本的代码可能与下面代码不一样,应该是更新了代码和api。 一、使用方法1.先申请一个按键结构。 2.初始化按键对象,绑定按键的GPIO电平读取接口read_button_pin() ,后一个参数设置有效触发电平。 3.注册按键事件。 4.启动按键。 5.设置一个5ms间隔的定时器循环调用后台处理函数。 //按键状态读取接口
unsigned char btn0_id = 0;
struct Button button0;
uint8_t read_button0_GPIO(void)
{
return (GPIO_ReadPin(BSP_PB_GPIO, BSP_PB_PIN));
}
void button_callback(void *button)
{
uint32_t btn_event_val;
btn_event_val = get_button_event((struct Button *)button);
switch(btn_event_val)
{
case PRESS_DOWN:
printf("---> key1 press down! <---\r\n");
break;
case PRESS_UP:
printf("***> key1 press up! <***\r\n");
break;
case PRESS_REPEAT:
printf("---> key1 press repeat! <---\r\n");
break;
case SINGLE_CLICK:
printf("---> key1 single click! <---\r\n");
break;
case DOUBLE_CLICK:
printf("***> key1 double click! <***\r\n");
break;
case LONG_PRESS_START:
printf("---> key1 long press start! <---\r\n");
break;
case LONG_PRESS_HOLD:
printf("***> key1 long press hold! <***\r\n");
break;
}
}
特性MultiButton 使用C语言实现,基于面向对象方式设计思路,每个按键对象单独用一份数据结构管理: struct Button {
uint16_t ticks;
uint8_t repeat: 4;
uint8_t event : 4;
uint8_t state : 3;
uint8_t debounce_cnt : 3;
uint8_t active_level : 1;
uint8_t button_level : 1;
uint8_t (*hal_button_Level)(void);
BtnCallback cb[number_of_event];
struct Button* next;
};
这样每个按键使用单向链表相连,依次进入 button_handler(struct Button* handle) 状态机处理,所以每个按键的状态彼此独立。 按键事件[td]事件 | 说明 | PRESS_DOWN | 按键按下,每次按下都触发 | PRESS_UP | 按键弹起,每次松开都触发 | PRESS_REPEAT | 重复按下触发,变量repeat计数连击次数 | SINGLE_CLICK | 单击按键事件 | DOUBLE_CLICK | 双击按键事件 | LONG_PRESS_START | 达到长按时间阈值时触发一次 | LONG_PRESS_HOLD | 长按期间一直触发 |
#include "main.h"
//按键状态读取接口
uint8_t read_button0_GPIO(void)
{
return (GPIO_ReadInputBit(KEY1_BUTTON_GPIO_PORT, KEY1_BUTTON_PIN));
}
unsigned char btn0_id = 0;
struct Button button0;
void button_callback(void *button)
{
uint32_t btn_event_val;
btn_event_val = get_button_event((struct Button *)button);
switch(btn_event_val)
{
case PRESS_DOWN:
printf("---> key1 press down! <---\r\n");
break;
case PRESS_UP:
printf("***> key1 press up! <***\r\n");
break;
case PRESS_REPEAT:
printf("---> key1 press repeat! <---\r\n");
break;
case SINGLE_CLICK:
printf("---> key1 single click! <---\r\n");
break;
case DOUBLE_CLICK:
printf("***> key1 double click! <***\r\n");
break;
case LONG_PRESS_START:
printf("---> key1 long press start! <---\r\n");
break;
case LONG_PRESS_HOLD:
printf("***> key1 long press hold! <***\r\n");
break;
}
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
//....初始化配置
printf("Hardware_Init [ok] \r\n");
printf("apm32f411 board testing 2024-05-06\r\n");
printf("apm32f411 board module multi-button\r\n");
//初始化按键对象
button_init(&button0, read_button0_GPIO, 0);
button_attach(&button0, PRESS_DOWN, button_callback);
button_attach(&button0, PRESS_UP, button_callback);
button_attach(&button0, PRESS_REPEAT, button_callback);
button_attach(&button0, SINGLE_CLICK, button_callback);
button_attach(&button0, DOUBLE_CLICK, button_callback);
button_attach(&button0, LONG_PRESS_START, button_callback);
button_attach(&button0, LONG_PRESS_HOLD, button_callback);
//启动按键
button_start(&button0);
while(1)
{
button_ticks();
Delay_ms(5);
}
}
测试效果定时器简测模仿例程和STM32进行了定时器中断试验。 #include "main.h"
#include "Bsp_Timer.h"
volatile uint32_t tick = 0;
unsigned int TIM2_LED=0;
/*
arr:自动重装值。
psc:时钟预分频数
定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft
Ft=定时器工作频率,单位:Mhz
例如:
APM_Timer1_Init(1000-1,84-1);/*定时器时钟84M,分频系数84,所以84M/84=100kHZ,计数1000次为1ms*/
*/
void APM_Timer1_Init(unsigned int arr,unsigned int psc)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
/* Enable TMR1 Periph Clock */
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
/* 配置TIM1 向上计数模式*/
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
TMR_BaseConfigStruct.division = arr;
TMR_BaseConfigStruct.period = psc;
TMR_BaseConfigStruct.repetitionCounter = 0;
TMR_ConfigTimeBase(TMR1, &TMR_BaseConfigStruct);
/* Enable TMR1 Interrupt */
TMR_EnableInterrupt(TMR1, TMR_INT_UPDATE);
NVIC_EnableIRQRequest(TMR1_UP_TMR10_IRQn, 0, 0);
TMR_Enable(TMR1);
}
void APM_Timer2_Init(unsigned int arr,unsigned int psc)
{
TMR_BaseConfig_T TMR_BaseConfigStruct;
/* Enable TMR1 Periph Clock */
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
/* Config TMR2 */
TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
TMR_BaseConfigStruct.division = arr;
TMR_BaseConfigStruct.period = psc;
TMR_BaseConfigStruct.repetitionCounter = 0;
TMR_ConfigTimeBase(TMR2, &TMR_BaseConfigStruct);
/* Enable TMR2 Interrupt */
TMR_EnableInterrupt(TMR2, TMR_INT_UPDATE);
NVIC_EnableIRQRequest(TMR2_IRQn, 0, 0);
TMR_Enable(TMR2);
}
/*!
* @brief This function handles TMR1 Update Handler
*
* @param None
*
* @retval None
*
*/
void TMR1_UP_TMR10_IRQHandler(void)
{
if(TMR_ReadIntFlag(TMR1, TMR_INT_UPDATE) == SET)
{
// tick++;
TMR_ClearIntFlag(TMR1, TMR_INT_UPDATE);
}
}
void TMR2_IRQHandler(void)
{
if(TMR_ReadIntFlag(TMR2, TMR_INT_UPDATE) == SET)
{
tick++;
if(tick==1000)
{
TIM2_LED=1;
tick=0;
}
TMR_ClearIntFlag(TMR2, TMR_INT_UPDATE);
}
}
|