本篇文章是根据B站UP主江科大自化协的教学视频STM32入门教程-2023持续更新中,在了解、学习与实操后整理的学习笔记,内容部分来自UP主的课程资料,并包含了一些个人的理解,如有谬误欢迎指正,详细知识点可以观看UP主的视频进行了解。
希望大家都能早日掌握单片机。
计算机的内存中每个存储单元都是有序的,且都是按字节编码的,字节是最小的存储单位。在程序中我们定义的变量存储于类似的缓存中,不过缓存位于CPU内部,且速度更快,同样每个存储单元也有对应编码,我们称其为地址,而指针即为地址。
指针是一种数据类型,是地址,指针变量即为存储地址的变量,其定义形式为:
//数据类型 *指针名
int *x;
float *f;
char *ch;
//如上,就分别定义了int、float和char类型的指针变量
在声明指针变量时,如果不一开始就赋值的话,可能会导致较为严重的错误:
int *p;
*p = 100;
//如上,因为指针变量无初值,“100”的值赋给的缓存地址是不确定(随机)的,这样有可能导致一些重要的缓存位置的数据被替换导致数据丢失出错
因此建议声明时赋空(“NULL”),避免出错:
int *p = NULL;//等同于int *p = 0;
*p = 100;
代码示例:
int a;//声明了一个普通变量a
int *pa;//声明一个指针变量,指向变量a的地址
pa = &a;//通过取地址符&,获取a的地址,赋值给指针变量
printf("%d",*pa);//通过间接寻址符,获取指针指向的内容
char str[6]={0,1,2,3,4,5};
char *P=str;
//输入第一个元素,以下两条语句功能相同
scanf("%s\n",str);
scanf("%s\n",p);
//打印第一个元素,以下两条语句功能相同
printf("%s",*str);
printf("%s",*p);
(C语言的术语lvalue左值指用于识别或定位一个存储位置的标识符,必须是可改变的。)
char str[6]={0,1,2,3,4,5};
char *P=str;
//输入第二个元素
//scanf("%s\n",str+1);此为错误的用法
scanf("%s\n",p+1);
//打印第二个元素
//printf("%s",*(str+1);此为错误的用法
printf("%s",*(p+1));
结构体是一种构造数据类型。数组是同种类型的数据的集合,那么结构体就是不同类型的数据组合成的整体。其定义形式如下:
struct 结构体名称
{
//结构体成员可以是一种基本的数据类型,也可以是数组,或者另一个结构体
结构体成员1;
结构体成员2;
结构体成员3;
……
};
结构体声明仅是进行一个框架的描绘,并不会为其分配存储空间, 直到定义一个结构体变量。
定义形式如下:
struct 结构体名称 结构体变量名
示例如下:
#include
struct Book
{
char title[120];
char author[40];
float price;
unsigned int date;
char publisher[40];
}
int main(void)
{
struct Book book;
return 0;
}
定义形式如下:
struct 结构体名称
{
//结构体成员可以是一种基本的数据类型,也可以是数组,或者另一个结构体
结构体成员1;
结构体成员2;
结构体成员3;
……
} 结构体变量名;
定义形式如下:
struct
{
//结构体成员可以是一种基本的数据类型,也可以是数组,或者另一个结构体
结构体成员1;
结构体成员2;
结构体成员3;
……
} 结构体变量名;
定义形式如下:
typedef struct 结构体名称
{
//结构体成员可以是一种基本的数据类型,也可以是数组,或者另一个结构体
结构体成员1;
结构体成员2;
结构体成员3;
……
} 简化的结构体变量定义格式("struct 结构体名称");
int main(void)
{
简化的结构体变量定义格式("struct 结构体名称") 结构体变量名;
}
示例如下:
typedef struct Book
{
char title[120];
char author[40];
float price;
unsigned int date;
char publisher[40];
} infor;
int main(void)
{
infor book;
return 0;
}
要访问结构体成员,我们需要引入一个新的运算符——点号(.)运算符。比如book.title就是引用book结构体的title成员,它是一个字符数组;而book.price则是引用book结构体的price成员,它是一个浮点型的变量。示例如下:
typedef struct{
char title[120];
char author[40];
float price;
unsigned int date;
char publisher[40];
} infor;
int main(void)
{
infor book;
book.title[120]="";
book.author[40]="";
book.price=;
book.date=;
book.publisher=;
}
要访问结构体成员,我们需要引入一个新的运算符——指针(->)运算符。
访问形式如下:
结构体指针(结构体的首地址)->结构体成员名=;//意为将值赋给结构体指针指向的结构体成员
//等同于:
(*结构体指针).结构体成员名=;//注意:“.”号运算符优先级比“*”号运算符高,因此要加括号
示例如下:
typedef struct{
char title[120];
char author[40];
float price;
unsigned int date;
char publisher[40];
} infor;
int main(void)
{
infor book;
struct *pt;
pt=&book;
pt->title[120]="";
pt->author[40]="";
pt->price=;
pt->date=;
pt->publisher[40]=;
}
枚举是C语言中的一种基本数据类型,它的用途是:定义一个取值受限制的整形变量,用于限制变量取值范围,让数据更为简洁和易读,提高可读性。其定义格式如下:
enum 枚举类型名称{
枚举值名称,
枚举值名称,
……
};
定义形式如下:
enum 枚举类型名称 枚举变量1,枚举变量2;
定义形式如下:
enum 枚举类型名称{
枚举值名称,
枚举值名称,
……
} 枚举变量名;
定义形式如下:
enum {枚举值名称,枚举值名称,……} 枚举变量名;
示例如下:
int main(void)
{
enum Week{Mon=1,Tue,Wed} day;
day = Wed;
return 0;
}
#include
enum WeekDay{Mon=1,Tue,Wed,Thu,Fri,Sat,Sun};
int main(void)
{
enum WeekDay day;
day = Mon; //day = 1;
day = Tue; //day = 2;
printf("%d\n",day);
return 0;
}
#include
enum Week{Mon=1, Tue, Wed, Thu, Fri, Sat, Sun} day;
int main(){
// 遍历枚举元素
for (day = Mon; day <= Sun; day++)
{
printf("枚举元素:%d\n", day);
}
}
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed; //定义枚举变量
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
}
//节选
STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器,常应用在嵌入式领域,如智能车、无人机、机器人、无线通信、物联网、工业控制、娱乐电子产品等。STM32功能强大、性能优异、片上资源丰富、功耗低,是一款经典的嵌入式微控制器。目前STM32共有四个系列,分别是高性能系列、主流系列、超低功耗系列和无线系列,对应的都有不同的产品,如:
系列:主流系列STM32F1
内核:ARM Cortex-M3
主频:72MHz
RAM:20K(SRAM)
ROM:64K(Flash)
供电:2.0~3.6V(标准3.3V)
封装:LQFP48
GPIO(General Purpose Input Output)通用输入输出口,可配置为8种输入输出模式,引脚电平:0V~3.3V,部分引脚可容忍5V。
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等。
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。
GPIO位结构由引脚端口、驱动器和寄存器组成。由图可以将结构分为两部分,一是上半部分,为输入模式;二是下半部分,为输出模式。
上拉电阻接通时,保证引脚为高电平;
下拉电阻接通时,保证引脚为低电平;
施密特触发器执行逻辑:如果输入电压大于某一阈值,输入就会瞬间升为高电平;如果输入电压小于某一阈值,输入就会瞬间降为低电平。
MOS管相当于一种电子开关,通过信号来控制开关的导通和关闭,开关负责使IO口输出高或低电平;
推挽输出模式(强推输出模式)下,高低电平均有较强的驱动能力,STM32对IO口有绝对的控制权;
开漏输出模式下,只有低电平有驱动能力,高电平没有驱动能力;常作为通信协议的驱动方式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰;还可用于输出5V的电平信号
通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式:
内容较多,见STM32F10XXX参考手册。
端口配置寄存器共16位,但每4位数据表示1位,共需要64位,而STM32中每个寄存器都为32位,因此分为端口配置低寄存器和端口配置高寄存器。通过端口配置寄存器可以配置GPIO工作模式与端口输出速度。
输出速度可以限制输出引脚的最大翻转速度,作用是降低功耗、提高稳定性,一般情况下配置为50MHz。
输入数据共16位,但寄存器共32位,因此寄存器高16位为空。
输出数据共16位,但寄存器共32位,因此寄存器高16位为空。
高16位用于清除,低16位用于设置。
高16位为空,低16位用于清除,方法同上。
高15位为空,低17位用于锁定,较少使用。
在RCC时钟控制的函数库中,我们最经常用到的是以下三个函数:
//AHB系统总线时钟控制
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
//APB2总线时钟控制
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
//APB1总线时钟控制
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
/*第一个参数为外设选择,与STM32互联型的设备在下列列表中选择:
* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_OTG_FS
* @arg RCC_AHBPeriph_ETH_MAC
* @arg RCC_AHBPeriph_ETH_MAC_Tx
* @arg RCC_AHBPeriph_ETH_MAC_Rx
*
* @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
*
* @arg RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
* RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
* RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
* RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_USART4,
* RCC_APB1Periph_USART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
* RCC_APB1Periph_USB, RCC_APB1Periph_CAN1, RCC_APB1Periph_BKP,
* RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC,
* RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14
*
* 其他设备在下列列表中选择:
* @arg RCC_AHBPeriph_DMA1
* @arg RCC_AHBPeriph_DMA2
* @arg RCC_AHBPeriph_SRAM
* @arg RCC_AHBPeriph_FLITF
* @arg RCC_AHBPeriph_CRC
* @arg RCC_AHBPeriph_FSMC
* @arg RCC_AHBPeriph_SDIO
*
*可用按位或(|)来选择多个外设
*
*第二个参数为选择使能或失能,选择:ENABLE or DISABLE
*/
GPIO连接在APB2总线上,且假定以PA0为接口,则使能GPIO的函数为:
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
在GPIO通用IO口的函数库中,我们最经常用到的是以下函数:
//复位函数,调用这个函数后,所指定的GPIO外设就会被复位
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//复位函数,可以复位AFIO外设
void GPIO_AFIODeInit(void);
//初始化函数,功能:用结构体的参数来初始化GPIO口
//初始化时,我们需要先定义一个结构体变量,然后给结构体赋值,最后调用初始化函数,函数内部会自动读取结构体的值,然后把外设的各个参数配置好。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//功能:把结构体变量赋一个默认值
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);
//以下四个函数为GPIO的读取函数,均有返回值
//功能:读取输入数据寄存器某一个端口的输入值
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//功能:读取整个输入数据寄存器
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
//功能:读取输出数据寄存器的某一位,用于输出模式下,查看输入的值
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
//功能:读取整个输出数据寄存器
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//以下四个函数为GPIO的写入函数
//功能:将指定端口引脚设置为高电平
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);
//功能:同时对16个端口进行写入操作
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
/*参数注释:
*①GPIO_TypeDef* GPIOx,GPIO端口选择,值为GPIOx(x=A~G);
*可用按位或(|)来选择多个端口
*
*②uint16_t GPIO_Pin,IO口引脚选择,一个GPIO端口有16个引脚,所以其值为GPIO_Pin_x(x=0~15)
*可用按位或(|)来选择多个引脚
*
*③BitAction BitVal,指定写入的数据值,这个参数可以是BitAction枚举中的一个值,值为:
* @arg Bit_RESET:清除端口值,即置低电平
* @arg Bit_SET:设置端口值,即置高电平
*
*④uint16_t PortVal,指定要写入端口输出数据寄存器的值
*
*⑤GPIO_InitTypeDef* GPIO_InitStruct,GPIO初始化结构体的地址
*/
/*初始化结构体定义:
*①定义一个结构体变量
*GPIO_InitTypeDef GPIO_InitStructure;
*结构体如下:
*typedef struct
*{
* uint16_t GPIO_Pin;
* GPIOSpeed_TypeDef GPIO_Speed;
* GPIOMode_TypeDef GPIO_Mode;
*}GPIO_InitTypeDef;
*由此可知该结构体有三个成员,分别为GPIO_Pin、GPIO_Speed和GPIO_Mode,下一步就是分别赋值
*
*②给结构体赋值:
*GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
*GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //使用GPIO的0号引脚
*GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
*
*GPIO_Mode,用于设置工作模式,其枚举如下:
*typedef enum
*{ GPIO_Mode_AIN = 0x0, //模拟输入模式(Analog IN)
* GPIO_Mode_IN_FLOATING = 0x04, //浮空输入模式
* GPIO_Mode_IPD = 0x28, //下拉输入模式(In Pull Down)
* GPIO_Mode_IPU = 0x48, //上拉输入模式(In Pull Up)
* GPIO_Mode_Out_OD = 0x14, //开漏输出模式(Out Open Drain)
* GPIO_Mode_Out_PP = 0x10, //推挽输出模式(Out Push Pull)
* GPIO_Mode_AF_OD = 0x1C, //复用开漏模式(Atl Open Drain)
* GPIO_Mode_AF_PP = 0x18 //复用推挽模式(Atl Push Pull)
*}GPIOMode_TypeDef;
*
*GPIO_Pin,选择引脚,值为GPIO_Pin_x(x=0~15和All)
*
*GPIO_Speed,选择输出速度,其枚举如下:
*typedef enum
*{
* GPIO_Speed_10MHz = 1,
* GPIO_Speed_2MHz,
* GPIO_Speed_50MHz
*}GPIOSpeed_TypeDef;
*常用速度为50MHz
*/
以用低电平驱动的方式点亮一个接在PA0口的LED,并使其闪烁为例。
GPIO连接在APB2总线上,且以PA0为接口,则使能时钟的函数为:
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
为了保证高低电平的强驱动能力,选用推挽输出,代码如下:
//定义结构体变量(局部变量)
GPIO_InitTypeDef GPIO_InitStructure;
//访问结构体变量(引出结构体成员)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //使用GPIO的0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
调用输入的函数,GPIO_SetBits()、GPIO_ResetBits()和GPIO_WriteBit()均可单独设置引脚低电平,代码如下:
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(250);
//通过强制转换为BitAction的枚举类型来直接输入高低电平
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)1);
Delay_ms(250);
LED有来两种驱动方式:高电平驱动和低电平驱动,对STM32来说,两种驱动方式均可,但是因为很多单片机或者芯片,都使用了高电平弱驱动、低电平强驱动的规则,这样可以在一定程度上避免高低电平冲突,所以常使用低电平驱动方式。
开漏输出模式下,只有低电平有驱动能力,高电平没有驱动能力,因此无法进行高电平驱动。
代码如下(LED连接PA0口):
#include "stm32f10x.h" // Device header
#include "Delay.h"
/*
*第一步,使用RCC开启GPIO的时钟
*第二步,使用GPIO_Init函数初始化GPIO
*第三步,使用输出或输入的函数控制GPIO口
*/
int main(void)
{
//第一步,开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步,初始化GPIO
//定义结构体变量(局部变量)
GPIO_InitTypeDef GPIO_InitStructure;
//访问结构体变量(引出结构体成员)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //使用GPIO的0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
//使用GPIO_Init函数初始化GPIO
GPIO_Init(GPIOA, &GPIO_InitStructure);
//第三步,控制GPIO口
while(1)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(250);
//通过强制转换为BitAction的枚举类型来直接输入高低电平
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)0);
Delay_ms(250);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)1);
Delay_ms(250);
}
}
实现基本流水灯(LED连接PA0~PA6),代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
//第一步,开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步,初始化GPIO
//定义结构体变量(局部变量)
GPIO_InitTypeDef GPIO_InitStructure;
//访问结构体变量(引出结构体成员)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //使用GPIO的所有引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
//使用GPIO_Init函数初始化GPIO
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
//低电平驱动,因此需要取反
GPIO_Write(GPIOA,~0x0001); //0000 0000 0000 0001
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002); //0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004); //0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008); //0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010); //0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020); //0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040); //0000 0000 0100 0000
Delay_ms(500);
}
}
实现往返流水灯,代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
//第一步,开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//第二步,初始化GPIO
//定义结构体变量(局部变量)
GPIO_InitTypeDef GPIO_InitStructure;
//访问结构体变量(引出结构体成员)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //使用GPIO的所有引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
//使用GPIO_Init函数初始化GPIO
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
unsigned int i;
for(i=0;i<7;i++)
{
GPIO_Write(GPIOA,~(0x0001<<i));
Delay_ms(250);
}
for(i=0;i<7i++)
{
GPIO_Write(GPIOA,~(0x0040>>i));
Delay_ms(250);
}
}
}
有源蜂鸣器同样使用低电平驱动,不过接口为PB12,代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
//第一步,开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//第二步,初始化GPIO
//定义结构体变量(局部变量)
GPIO_InitTypeDef GPIO_InitStructure;
//访问结构体变量(引出结构体成员)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //使用GPIO的12号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度为50MHz
//使用GPIO_Init函数初始化GPIO
GPIO_Init(GPIOB, &GPIO_InitStructure);
//第三步,控制GPIO口
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}
按键1连接PB1,按键2连接PB11;LED1连接PA1,LED2连接PA2,使用模块化编程,代码如下:
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
//RCC使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//使两个端口默认高电平,使LED处于未点亮的状态
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);
}
//点亮对应的LED
void LED_ON(unsigned char i)
{
if(i==1)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
if(i==2)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
}
//熄灭对应的LED
void LED_OFF(unsigned char i)
{
if(i==1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
if(i==2)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
}
//通过检测输出数据寄存器的数据,得到当前的LED状态,再用相反作用的函数使其状态变化
void LED_Turn(unsigned char i)
{
if(i == 1)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0)//检测输出数据寄存器的数据是否为0
{
GPIO_SetBits(GPIOA,GPIO_Pin_1); //若为0,即LED点亮时,对应数据位置1,即熄灭
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
}
if(i == 2)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0)//检测输出数据寄存器的数据是否为0
{
GPIO_SetBits(GPIOA,GPIO_Pin_2); //若为0,即LED点亮时,对应数据位置1,即熄灭
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
}
}
GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
//RCC使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
//消除抖动后返回对应键码
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0);
Delay_ms(20); //消除抖动
KeyNum = 1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0);
Delay_ms(20); //消除抖动
KeyNum = 2;
}
return KeyNum;
}
③main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum;
int main(void)
{
LED_Init(); //LED端口初始化
Key_Init(); //按键端口初始化
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
LED_Turn(1);
}
if(KeyNum == 2)
{
LED_Turn(2);
}
}
}
蜂鸣器连接PB12口,光敏传感器连接PB13口,使用模块化编程,代码如下:
①Buzzer.c
#include "stm32f10x.h" // Device header
void Buzzer_Init(void)
{
//RCC使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//使蜂鸣器默认静默
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
//蜂鸣器鸣响
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
//蜂鸣器静默
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
//蜂鸣器状态翻转
//通过检测输出数据寄存器的数据,得到当前的蜂鸣器状态,后使用函数改变蜂鸣器当前状态
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12) == 0)//检测输出数据寄存器的数据是否为0
{
GPIO_SetBits(GPIOB,GPIO_Pin_12); //若为0,即蜂鸣器鸣响,对应数据位置1,即使蜂鸣器静默
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
}
②LightSensor.c
#include "stm32f10x.h" // Device header
//光敏传感器初始化
void LightSensor_Init(void)
{
//RCC使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //使用上拉输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//返回光敏传感器的电平情况
uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}
③main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
int main(void)
{
Buzzer_Init(); //蜂鸣器初始化
LightSensor_Init(); //光敏传感器初始化
while(1)
{
if(LightSensor_Get() ==1)//如果光敏传感器接收的亮度低于阈值,则蜂鸣器鸣响
{
Buzzer_ON();
}
else
{
Buzzer_OFF();
}
}
}