STM32之旅
学习了51单片机后,就要接触到更高级一点的单片机了,比如STM32,ST也有很多款单片机,现在用比较基础的学习——STM32F103RCT6。
一、LED驱动
hal库的使用比较简单,可以直接在STM32CubeMX中分配好IO之后一键生成工程,为了提高程序的可读性,自己写一个头文件,在调用过程中会比较简单。
drv_led.h:
#ifndef __DRV_LED_H
#define __DRV_LED_H
#define LED1_PIN GPIO_PIN_8
#define LED1_PORT GPIOA
#define LED2_PIN GPIO_PIN_2
#define LED2_PORT GPIOD
#define LED1_ON HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET)
#define LED1_OFF HAL_GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET)
#define LED2_ON HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_RESET)
#define LED2_OFF HAL_GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_SET)
#endif
二、按键驱动
几乎每个项目都有用到按键,为了避免以后在做大项目的时候还在琢磨按键怎么写,现在写一个,方便以后使用。
drv_key.h:
#ifndef __DRV_KEY_H
#define __DRV_KEY_H
#define RESET 0
#define SET 1
#define WK_UP_PIN GPIO_PIN_0
#define WK_UP_PORT GPIOA
#define KEY1_PIN GPIO_PIN_5
#define KEY1_PORT GPIOC
#define KEY2_PIN GPIO_PIN_15
#define KEY2_PORT GPIOA
#define WK_UP HAL_GPIO_ReadPin(WK_UP_PORT,WK_UP_PIN)
#define KEY1 HAL_GPIO_ReadPin(KEY1_PORT,KEY1_PIN)
#define KEY2 HAL_GPIO_ReadPin(KEY2_PORT,KEY2_PIN)
void key_scan(void);
#endif
drv_key.c:
#include "stm32f1xx.h"
#include "drv_led.h"
#include "drv_key.h"
void key_scan(void)
{
if(RESET == KEY1)
{
HAL_Delay(10); //消抖
if(RESET == KEY1)
{
/********事件区***********/
ED1_ON;
/********事件区***********/
while(RESET == KEY1); //松手反应
}
}
if(RESET == KEY2)
{
HAL_Delay(10); //消抖
if(RESET == KEY2)
{
/********事件区***********/
LED2_ON;
/********事件区***********/
while(RESET == KEY2); //松手反应
}
}
}
三、时钟树
STM32F1是M3内核,它的时钟数很庞大,让一个初学者去看,估计会很吃力,和我们入门的8051单片机的时钟不同,这里又倍频、又分频,而且还分成好多个时钟,不同的外设时钟不一样。
总感觉不都明了,后来在STM32CubeMX中看到了时钟配置,这个看起来就明了多了
四、USART
串口也是用的比较多的,在STM32CubeMX中生成代码后,需要添加一些代码才可以用。
drv_usart.h:
#ifndef __DRV_USART_H
#define __DRV_USART_H
#define USART1_MAX_LEN 64 //接收区长度
#define USART1_BUFF_CACHE_LEN 1 //接收缓冲区长度
extern uint16_t g_usart1_sta; //接收状态[1:15],最高位为接收完成标志
extern uint8_t g_usart1_buff[USART1_MAX_LEN]; //接收buff
extern uint8_t g_usart1_buff_cache[USART1_BUFF_CACHE_LEN]; //接收缓存
#endif
使用printf()发送的时候需要重定向,没有fputc()是不行的;使用中断接收的时候,并不是在USART1_IRQHandler()里面添加代码,而是在回调函数HAL_UART_RxCpltCallback()中写,接收完成时要在后面加上HAL_UART_Receive_IT(),串口初始化时也要加上HAL_UART_Receive_IT(),否则不能进中断。
drv_usart.c:
#include "stm32f1xx.h"
#include "drv_usart.h"
#include "stdio.h"
#include "usart.h"
uint16_t g_usart1_sta;//接收状态[1:15],最高位为接收完成标志
uint8_t g_usart1_buff[USART1_MAX_LEN];
uint8_t g_usart1_buff_cache[USART1_BUFF_CACHE_LEN];
/***********printf函数重写,有了这个函数就可以使用printf()发送串口数据了**********/
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return 0;
}
/****************************串口中断回调函数*************************************/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(USART1 == huart->Instance)
{
if(0 == (g_usart1_sta & 0x8000))//接收未完成
{
g_usart1_buff[g_usart1_sta++] = g_usart1_buff_cache[0];
if(0x0A == g_usart1_buff_cache[0])//如果接收到回车,就接收完成
{
g_usart1_sta |= 0x8000;
}
}
HAL_UART_Receive_IT(&huart1,(uint8_t *)g_usart1_buff_cache,USART1_BUFF_CACHE_LEN);
}
}
这里没加HAL_UART_Receive_IT()是不会进进中断的。
usart.c:
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Receive_IT(&huart1,g_usart1_buff_cache,USART1_BUFF_CACHE_LEN);
}