IO篇
gpio类型
#define GPIO_MODE_INPUT/*浮空输入模式*/
#define GPIO_MODE_OUTPUT_PP/*推挽输出模式*/
#define GPIO_MODE_OUTPUT_OD/*开漏输出模式*/
#define GPIO_MODE_AF_PP/*复用推挽输出模式*/
#define GPIO_MODE_AF_OD/*复用开漏输出模式*/
#define GPIO_MODE_AF_INPUT/*复用输入模式*/
#define GPIO_MODE_ANALOG /*模拟输入模式*/
#define GPIO_MODE_IT_RISING /*具有上升沿触发检测的外部中断模式*/
#define GPIO_MODE_IT_FALLING /*具有下降沿触发检测的外部中断模式*/
#define GPIO_MODE_IT_RISING_FALLING /*具有上升/下降沿触发检测的外部中断模式*/
#define GPIO_MODE_EVT_RISING /*具有上升沿触发检测的外部事件模式*/
#define GPIO_MODE_EVT_FALLING /*带有下降沿触发检测的外部事件模式*/
#define GPIO_MODE_EVT_RISING_FALLING /*具有上升/下降沿触发检测的外部事件模式*/
中断篇
配置步骤
1、使能时钟
2、设置GPIO工作模式,触发条件,开启AFIO时钟,设置IO口与中断线的映射关系
3、配置中断优先级(NVIC),并使能中断
4、编写中断服务函数
5、编写中断处理回调函数
1-2初始化
这段要放进main里初始化
__HAL_RCC_GPIOE_CLK_ENABLE();
触发条件:
gpio_init_struct.Mode=GPIO_MODE_IT_FALLING;
gpio_init_struct.Pin=GPIO_PIN_4;
gpio_init_struct.Pull=GPIO_PULLUP;
gpio_init_struct.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOE,&gpio_init_struct);
3配置中断优先级
/* 抢占0,子优先级2 */
HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 2);
/* 使能中断线4 */
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
4编写中断服务函数
0-4独立,5-9共用,10-15共用
void EXTI4_IRQHandler(void)
{
/* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
/* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
}
5编写中断处理回调函数
注意:该函数会自动被4中中断函数调用
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
}
串口篇
配置步骤
1、串口参数初始化(波特率、字长、奇偶校验等)
2、使能串口和GPIO口时钟
3、GPIO模式设置(速度、上下拉、复用功能等)
4、开启串口相关中断,配置串口中断优先级
5、编写中断服务函数
6、串口数据接收和发送
1、串口参数初始化(波特率、字长、奇偶校验等)
#define USART_REC_LEN 200 /* 定义最大接收字节数 200 */
#define USART_EN_RX 1 /* 使能(1)/禁止(0)串口1接收 */
#define RXBUFFERSIZE 1 /* 缓存大小 */
UART_HandleTypeDef g_uart1_handle; /* HAL UART句柄 */
uint8_t g_usart_rx_buf[USART_REC_LEN]; /* 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 */
uint16_t g_usart_rx_sta; /* 接收状态标记 */
uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库USART接收Buffer */
void usart_init(uint32_t baudrate)
{
/*UART 初始化设置*/
g_uart1_handle.Instance = USART_UX; /* USART_UX */
g_uart1_handle.Init.BaudRate = baudrate; /* 波特率 */
g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
g_uart1_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
g_uart1_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
g_uart1_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&g_uart1_handle); /* HAL_UART_Init()会使能UART1 */
/* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
2-4使能串口和GPIO口时钟
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if (huart->Instance == USART_UX) /* 如果是串口1,进行串口1 MSP初始化 */
{
__HAL_RCC_USART1_CLK_ENABLE(); /* 使能串口时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /* 使能串口TX、X脚时钟 */
gpio_init_struct.Pin = USART_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = USART_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */
HAL_NVIC_EnableIRQ(USART_UX_IRQn); /* 使能USART1中断通道 */
HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3); /* 组2,最低优先级:抢占优先级3,子优先级3 */
}
}
5、编写中断服务函数
void USART_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
}
6、回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART_UX) /* 如果是串口1 */
{
if ((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if (g_usart_rx_sta & 0x4000) /* 接收到了0x0d(即回车键) */
{
if (g_rx_buffer[0] != 0x0a) /* 接收到的不是0x0a(即不是换行键) */
{
g_usart_rx_sta = 0; /* 接收错误,重新开始 */
}
else /* 接收到的是0x0a(即换行键) */
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到0X0d(即回车键) */
{
if (g_rx_buffer[0] == 0x0d)
g_usart_rx_sta |= 0x4000;
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
HAL_UART_Receive_IT(&uart_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
}
7、串口数据接收和发送
中断接收:
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
发送:
HAL_UART_Transmit(&g_uart1_handle,(uint8_t*)g_usart_rx_buf, len, 1000);
printf("");
注意该函数需要重定义
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART_UX->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART_UX->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
加入以下不需要开启Use MicroLIB
#if (__ARMCC_VERSION >= 6010050) /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t"); /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */
#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#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. */
};
#endif
/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART_UX->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART_UX->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
函数篇
中断服务函数
void EXTI4_IRQHandler(void)//外部中断
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);
}
void USART_UX_IRQHandler(void)//串口中断
{
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
}
void WWDG_IRQHandler(void)//串口看门狗
{
HAL_WWDG_IRQHandler(&g_wwdg_handle); /* 调用WWDG共用中断处理函数 */
}
中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//外部中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)//窗口看门狗
独立看门狗
配置步骤
1、取消寄存器写保护,设置预分频系数和重装载值
2、重载计数值喂狗
3、启动看门狗
1、3、取消寄存器写保护,设置预分频系数和重装载值
由于HAL_IWDG_Init函数中已经有了取消写保护,所以不用额外再写
IWDG_HandleTypeDef g_iwdg_handle; /* 独立看门狗句柄 */
void iwdg_init(uint8_t prer, uint16_t rlr)
{
g_iwdg_handle.Instance = IWDG;
g_iwdg_handle.Init.Prescaler = prer; /* 设置IWDG分频系数 */
g_iwdg_handle.Init.Reload = rlr; /* 重装载值 */
HAL_IWDG_Init(&g_iwdg_handle); /* 初始化IWDG并启动 */
}
2、重载计数值喂狗
HAL_IWDG_Refresh(&g_iwdg_handle); /* 重装载计数器 */
计算
分频64,重装值625,时间大约是1s
则频率应该是40k,则(1/40k)64=0.0016s
重装值6250.0016=1s
窗口看门狗
配置步骤
1、使能时钟
2、设置窗口值,分频数和计算器初始值
3、开启wwdg
4使能中断通道并配置优先级(如果开启了wwdg中断)
5、编写中断服务函数
6、重写串口看门狗唤醒中断处理回调函数
23初始化
WWDG_HandleTypeDef g_wwdg_handle; /* WWDG句柄 */
void wwdg_init(uint8_t tr, uint8_t wr, uint32_t fprer)
{
g_wwdg_handle.Instance = WWDG;
g_wwdg_handle.Init.Prescaler = fprer; /* 设置分频系数 */
g_wwdg_handle.Init.Window = wr; /* 设置窗口值 */
g_wwdg_handle.Init.Counter = tr; /* 设置计数器值 */
g_wwdg_handle.Init.EWIMode = WWDG_EWI_ENABLE; /* 使能窗口看门狗提前唤醒中断 */
HAL_WWDG_Init(&g_wwdg_handle); /* 初始化WWDG */
}
Fwwdg=PCLK1/(4096*2^fprer). 一般PCLK1=36Mhz
14
void HAL_WWDG_MspInit(WWDG_HandleTypeDef *hwwdg)
{
__HAL_RCC_WWDG_CLK_ENABLE(); /* 使能窗口看门狗时钟 */
HAL_NVIC_SetPriority(WWDG_IRQn, 2, 3); /* 抢占优先级2,子优先级为3 */
HAL_NVIC_EnableIRQ(WWDG_IRQn); /* 使能窗口看门狗中断 */
}
5中断服务函数
void WWDG_IRQHandler(void)
{
HAL_WWDG_IRQHandler(&g_wwdg_handle); /* 调用WWDG共用中断处理函数 */
}
6回调函数
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
HAL_WWDG_Refresh(&g_wwdg_handle); /* 更新窗口看门狗值 */
LED1_TOGGLE(); /* LED1闪烁 */
}
基本定时器
对于STM32F103RET6
基本定时器6、7;
通用定时器2、3、4、5;
高级定时器1、8;
配置步骤
1、定时器使能
2、初始化定时器参数、设置自动重装值,分频系数,计数方式
3、使能定时器中断,开启定时器计数,配置定时器中断优先级
4、编写中断服务函数
2初始化
TIM_HandleTypeDef g_timx_handle; /* 定时器句柄 */
void btim_timx_int_init(uint16_t arr, uint16_t psc)
{
g_timx_handle.Instance = TIM6; /* 通用定时器X */
g_timx_handle.Init.Prescaler = 7200-1; /* 设置预分频系数 */
g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_handle.Init.Period = 5000-1; /* 自动装载值 */
HAL_TIM_Base_Init(&g_timx_handle);
HAL_TIM_Base_Start_IT(&g_timx_handle); /* 使能定时器x及其更新中断 */
}
13使能中断
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE(); /* 使能TIM时钟 */
HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3); /* 抢占1,子优先级3,组2 */
HAL_NVIC_EnableIRQ(TIM6_IRQn); /* 开启ITM3中断 */
}
}
中断服务函数
void TIM6_DAC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_handle);
}
中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM6)
{
LED1_TOGGLE();
}
}
通用定时器
注意!!!
1、基本定时器用TIM6_DAC_IRQHandler,但是到了通用定时器就变了,而是TIM2_IRQHandler.
2、看代码
void TIM2_IRQHandler(void)
{
LED1_TOGGLE();
__HAL_TIM_CLEAR_FLAG(&tim2_handle,TIM_IT_UPDATE);
}
这个直接在中断服务函数处理了,所以这里需要手动清除标志
使用回调函数时会自动清理标志。
PWM
配置步骤
1、开启定时器、通道输出的时钟,配置该IO口复用
2、初始化tim,设置arr、psc等参数
3、设置timx_CHy的PWM函数,输出比较极性、比较值等参数。
4、使能timx,使能tim chy输出
5、修改timx——ccr2来控制占空比
直接上代码
TIM_HandleTypeDef g_timx_pwm_chy_handle; /* 定时器x句柄 */
TIM_OC_InitTypeDef timx_oc_pwm_chy = {0}; /* 定时器PWM输出配置 */
g_timx_pwm_chy_handle.Instance = TIM3; /* 定时器x */
g_timx_pwm_chy_handle.Init.Prescaler = 72; /* 定时器分频 */
g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_pwm_chy_handle.Init.Period = 500; /* 自动重装载值 */
HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle); /* 初始化PWM */
timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM1 */
timx_oc_pwm_chy.Pulse = 250; /* 设置比较值,此值用来确定占空比 */
/* 默认比较值为自动重装载值的一半,即占空比为50% */
timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW; /* 输出比较极性为低 */
HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2); /* 配置TIMx通道y */
HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2); /* 开启对应PWM通道 */
while (1)
{
delay_ms(10);
if(flag1==0)
{
if(++num==300)
{
flag1=1;
};
}
else{
if(--num==0)
{
flag1=0;
}
}
__HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle,TIM_CHANNEL_2,num);
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_5;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOB,&gpio_init_struct);
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_AFIO_REMAP_TIM3_PARTIAL();
}
}
定时器捕获
配置步骤
1、开启tim和输入通道gpio时钟,配置该io口复用功能输入
2、初始化tim,设置tim的arr、psc
3、设置timx_chy输入捕获模式,开启输入捕获
4、使能定时器中断,开启捕获功能以捕获中断,配置定时器中断优先级
5、编写中断服务函数