单片机小白,迄今为止第三次学习单片机(前两次点完灯,因为一些其他的事情就没有继续学习了),是个点灯带师。这学期刚好开了单片机的课程,因此希望通过写笔记的方式督促自己的学习。目前除了听自己老师上课外,还在B站跟着江科大的教程学习,此笔记是基于江科大STM32教程,大部分原理图片均为江科大ppt里复制来的。但是,本笔记案例相较于江科大的有些许改进, 为锻炼自己的代码能力,对多个单独的模块进行统一的控制,对于改进的代码也有相应的实物接线图和说明。
江科大STM32教程链接:【STM32入门教程-2023持续更新中】 https://www.bilibili.com/video/BV1th411z7sn/?share_source=copy_web&vd_source=ee06a25b3dfb2900ab707b01fdff6667
GPIO(General Purpose Input Output) 通用输入输出口
可配置为8种输入输出模式
引脚电平: 0V~3.3V, 部分引脚可容忍5V
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
通过配置GPIO的端口配置寄存器,端口配置寄存器,端口可以配置成以下8种模式
模式名称 |
性质 |
特征 |
浮空输入 |
数字输入 |
可读取引脚电平,若引脚悬空,则电平不确定 |
上拉输入 |
数字输入 |
可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
下拉输入 |
数字输入 |
可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
模拟输入 |
模拟输入 |
GPIO无效,引脚直接接入内部ADC |
开漏输入 |
数字输入 |
可输出引脚电平,高电平为高阻态,低电平接VSS (高电平没有驱动能力) |
推挽输入 |
数字输入 |
可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输入 |
数字输入 |
由片上外设控制,高电平为高阻态,低电平接VSS (高电平没有驱动能力) |
复用推挽输入 |
数字输入 |
由片上外设控制,高电平接VDD,低电平接VSS |
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/**
* @brief Enables or disables the High Speed APB (APB2) peripheral clock.
* @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
* This parameter can be any combination of the following values:
* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
* RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
* RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
* RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
* RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
* RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
* RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11
* @param NewState: new state of the specified peripheral clock.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)函数, 用来使能或失能APB2外设时钟。
第一个参数填需要使能或者失能的外设,第二个参数填ENABLE or DISABLE
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
unsigned int gpio_groups=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Pin=gpio_groups;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
通过GPIO_InitTypeDef结构体配置GPIO的初始化参数
结构体内共有三个参数: GPIO_Pin GPIO_Speed GPIO_Mode
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
typedef enum
{ GPIO_Mode_AIN = 0x0, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
}GPIOMode_TypeDef;
GPIO_Init(GPIOA, &GPIO_InitStructure);
第一个参数: 需要初始化GPIO端口, GPIO初始化结构体
// 引脚高电平
GPIO_SetBits(GPIOA, gpio_groups);
while(1)
{
if(idx==7){
flag=0;
}
if(idx==0){
flag=1;
}
//引脚低电平
GPIO_ResetBits(GPIOA, leds[idx]);
// 蜂鸣器
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
// 延时
Delay_ms(500);
// 引脚高电平
GPIO_SetBits(GPIOA, leds[idx]);
// 蜂鸣器
GPIO_SetBits(GPIOA, GPIO_Pin_8);
// 延时
Delay_ms(500);
if(flag==1){
idx++;
}
if(flag==0){
idx--;
}
}
#include "stm32f10x.h"
#include "delay.h"
// LED 闪烁实验 蜂鸣器实验
// 2023年3月8日20:01:30
int main(void)
{
unsigned leds[8] = {GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, GPIO_Pin_3, GPIO_Pin_4,
GPIO_Pin_5, GPIO_Pin_6, GPIO_Pin_7};
unsigned flag = 1;
unsigned idx = 0;
// RCC 使能APB2外设 GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO初始化参数
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
unsigned int gpio_groups=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Pin=gpio_groups;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
// 执行GPIO初始化函数
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 引脚高电平
GPIO_SetBits(GPIOA, gpio_groups);
while(1)
{
if(idx==7){
flag=0;
}
if(idx==0){
flag=1;
}
//引脚低电平
GPIO_ResetBits(GPIOA, leds[idx]);
// 蜂鸣器
GPIO_ResetBits(GPIOA, GPIO_Pin_8);
// 延时
Delay_ms(500);
// 引脚高电平
GPIO_SetBits(GPIOA, leds[idx]);
// 蜂鸣器
GPIO_SetBits(GPIOA, GPIO_Pin_8);
// 延时
Delay_ms(500);
if(flag==1){
idx++;
}
if(flag==0){
idx--;
}
}
}
可以看出当光线较暗时,光敏电阻阻值增大,AO输出高电平。
A0-A7接LED,蜂鸣器I/O口接A9; 两个按键分别接到A10、B10,一个高电平触发(A10),一个低电平触发(B10); 光敏传感器模块数字输出口DO接A8; VCC和GND的接线不做说明。
#include "stm32f10x.h"
#include "delay.h"
#include "LED.h"
#include "key.h"
#include "lightSensor.h"
#include "beep.h"
// 按键控制LED流水灯和光敏传感器控制蜂鸣器
// 2023年3月10日16:10:49
int main(void)
{
Init_LED(); // 初始化LED 输出IO口
Init_Key(); // 初始化按键 输入IO口
Init_LightSensor(); // 初始化光敏传感器 输出IO口
Init_Beep(); // 初始化蜂鸣器 输入IO口
uint8_t mode = 0;
while(1)
{
uint8_t sensorSignal = Get_SignalFromSensor(); // 光敏传感器获取信号
uint8_t keyNum = Get_KeyNum(); // 按键序号
//按键控制流水灯
/*
if(keyNum==1){
mode = 1;
}else if(keyNum==2){
mode = 0;
}
*/
mode = keyNum==1 ? 1 : keyNum==2 ? 0 : mode;
mode = keyNum ? keyNum%2 : mode;
/*
if(mode==1){
FlowLed();
}else if(mode==0){
Close_All_Lights();
}
*/
mode==1 ? FlowLed(): Close_All_Lights();
// 光敏传感器控制蜂鸣器
/*
if(sensorSignal==1){
Beep_On();
}else if(sensorSignal==0){
Beep_Off();
}
*/
sensorSignal==1 ? Beep_On() : Beep_Off();
}
}
#include "stm32f10x.h"
#include "delay.h"
// 2023年3月10日17:00:13
// B10设置为上拉输入 默认高电平 按键按下后为低电平
// A10设置为下拉输入 默认低电平 按键按下后为高电平
void Init_Key(void)
{
// GPIO初始化结构体配置参数
GPIO_InitTypeDef GPIO_InitStructureA;
GPIO_InitTypeDef GPIO_InitStructureB;
// RCC时钟使能APB2外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置参数
GPIO_InitStructureA.GPIO_Mode=GPIO_Mode_IPD; // 下拉输入
GPIO_InitStructureA.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructureA.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructureB.GPIO_Mode=GPIO_Mode_IPU; // 上拉输入
GPIO_InitStructureB.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructureB.GPIO_Pin=GPIO_Pin_10;
// 调用初始化函数
GPIO_Init(GPIOA, &GPIO_InitStructureA);
GPIO_Init(GPIOB, &GPIO_InitStructureB);
return;
}
// 判断哪个按键按下
uint8_t Get_KeyNum(void)
{
uint8_t KeyNum=0;
// A10按下 下拉输入 默认低电平 按键按下后为高电平
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1){
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1);
Delay_ms(20);
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==0){
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_10)==1);
Delay_ms(20);
KeyNum=2;
}
return KeyNum;
}
#include "stm32f10x.h"
#include "delay.h"
// 初始化LED所用引脚 A0-A7
void Init_LED(void)
{
// 八个引脚 A0-A7
uint16_t GPIO_GROUP=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
|GPIO_Pin_6|GPIO_Pin_7;
// RCC 使能APB2外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO初始化结构体配置参数
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 推挽输出模式
GPIO_InitStructure.GPIO_Pin=GPIO_GROUP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
// 调用GPIO初始化函数
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 设置高电平
GPIO_SetBits(GPIOA, GPIO_GROUP);
return;
}
// 流水灯
uint8_t FlowLed(void)
{
static uint8_t flag=1;
static uint8_t pin=0;
uint16_t gpio_group[8] = {GPIO_Pin_0, GPIO_Pin_1, GPIO_Pin_2, GPIO_Pin_3, GPIO_Pin_4,
GPIO_Pin_5, GPIO_Pin_6, GPIO_Pin_7};
if((pin)==7){
flag=0;
}
if((pin)==0){
flag=1;
}
// 引脚低电平
GPIO_ResetBits(GPIOA, gpio_group[pin]);
// 延时
Delay_ms(500);
// 引脚高电平
GPIO_SetBits(GPIOA, gpio_group[pin]);
// 延时
Delay_ms(500);
if(flag==1){
pin = pin+1;
}
if(flag==0){
pin = pin-1;
}
return 1;
}
// 关闭所有LED
uint8_t Close_All_Lights(void)
{
uint16_t GPIO_GROUP=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5
|GPIO_Pin_6|GPIO_Pin_7;
GPIO_SetBits(GPIOA, GPIO_GROUP);
return 1;
}
#include "stm32f10x.h"
// 2023年3月10日21:24:36
// 初始化A8引脚作为光敏传感器信号的输入
// 当光线变暗时光敏电阻阻值增大,分压上升,输出高电平
void Init_LightSensor(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// RCC 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO初始化结构体变量
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD; // 下拉模式 默认输入为低电平
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
// 调用GPIO初始化函数
GPIO_Init(GPIOA, &GPIO_InitStructure);
return;
}
// 获取并光敏传感器输入到单片机里的信号
uint8_t Get_SignalFromSensor(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
}
#include "stm32f10x.h"
#include "delay.h"
// 2023年3月10日21:26:17
// 通过A9引脚输出的信号控制蜂鸣器
// 初始化所需要的引脚
void Init_Beep(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// RCC时钟使能APB2外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置初始化结构体的参数
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
// 调用GPIO初始化函数
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 设置高电平
GPIO_SetBits(GPIOA, GPIO_Pin_9);
return;
}
// 蜂鸣器鸣叫
uint8_t Beep_On(void)
{
GPIO_SetBits(GPIOA, GPIO_Pin_9);
Delay_ms(200);
GPIO_ResetBits(GPIOA, GPIO_Pin_9);
Delay_ms(200);
return 1;
}
uint8_t Beep_Off(void)
{
// 高电平停止
GPIO_SetBits(GPIOA, GPIO_Pin_9);
return 1;
}
函数名称 |
功能 |
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); |
根据GPIO结构体函数中的参数(GPIO_Mode, GPIO_Pin, GPIO_Speed)初始化外设GPIOx寄存器 |
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
设置指定的数据端口位(设置为高电平) |
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_pin); |
清除指定的数据端口位 |
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_pin, BitAction BitVal); |
设置或清除指定的数据端口位 |
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); |
向指定的GPIO数据端口写入数据 |
void GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
读取指定端口引脚的输入 |
void GPIO_ReadInputData(GPIO_TypeDef* GPIOx); |
读取指定的GPIO端口输入 |
void GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); |
读取指定端口引脚的输出 |
void GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); |
读取指定的GPIO端口输出 |