STM32学习笔记【江科协】【3-2】LED闪烁&LED流水灯&蜂鸣器

 根据接线图接线,这里采用低电平的接线方式


速通新建工程

还要新建一个System,存放系统资源

1.Start启动文件

找到固件库文件,打开启动文件 放在start的文件夹下 

这三个文件同样复制到start文件夹下

2. Library文件夹

找到标准外设驱动文件夹

3.Project文件夹

 main.c直接复制过来,就不用新建文件了

到此为止,工程文件的复制就完成了

4.System文件夹

 5.工程组里的文件添加

把默认组删掉,再新建三个组还有一个System

 

6.工程选项配置

这里是把System给加上

在调试器右端的setting里

6.main函数

加上头文件,写上主函数

#include "stm32f10x.h"                  // Device header

int main(void)
{	
	while(1)
	{	
		
	}
}

到这里工程就搭建完毕了


*小工具

 双击一下keilkill


点亮LED

操作STM32的GPIO需要三个步骤:1.使用RCC打开GPIO的时钟;2.使用GPIO_Init函数初始化GPIO;3.使用输出或输入函数来控制GPIO口

这里涉及了GPIO和RCC两个外设,可以看看这两个外设有哪些库函数

RCC库函数

RCC最常用的库函数有三个

右键点击函数名,跳转到函数定义 

 GPIO库函数

 

 void GPIO_DeInit(GPIO_TypeDef* GPIOx);

*调用这个函数,所指定的GPIO就会被复位
void GPIO_AFIODeInit(void);

*可以复位AFIO外设
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

*用结构体的参数来初始化GPIO端口,需定义一个结构体变量,然后再给GPIO赋值,最后再调用这个函数,这个函数就会自动读取结构体的值,自动把外设各个参数配好

*这种Init函数几乎在所有外设中都有,一般初始化外设都是使用Init函数来完成的
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);

*把结构体变量默认的赋一个值
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);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

*以上四个是GPIO的写入函数

*这些函数可以实现读取GPIO口的功能

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

这样时钟就开启了

 第一个参数根据跳转的定义填写为GPIOA一个小细节,

GPIO_InitTypeDef GPIO_Initstructure;这个结构体是一个局部变量

 

 GPIO_Mode_AIN = 0x0,

*AIN(Analog IN)模拟输入
  GPIO_Mode_IN_FLOATING = 0x04,

*IN_FLOATING是浮空输入
  GPIO_Mode_IPD = 0x28,

*IPD(In Pull Down)是下拉输入
  GPIO_Mode_IPU = 0x48,

*IPU(In Pull Up)是上拉输入
  GPIO_Mode_Out_OD = 0x14,

*Out_OD(Out Open Drain)是开漏输出
  GPIO_Mode_Out_PP = 0x10,

*Out_PP(Out Push Pull)是推挽输出
  GPIO_Mode_AF_OD = 0x1C,

*AF_OD(Atl Open Drain)是复用开漏
  GPIO_Mode_AF_PP = 0x18 

*AF_PP(Atl Push Pull)是复用推挽

点灯使用的是推挽输出,所以复制GPIO_Mode_Out_PP

然后再配置GPIO_Initstructure.GPIO_Pin,同样跳转定义

第三个GPIO_Initstructure.GPIO_Speed是一样的套路,选择GPIO_Speed_50MHz

注意取地址!!!

这样初始化就完成了,当GPIO_Init函数执行完,就实现以上内容

 内部的主要执行逻辑是,读取结构体参数,执行一堆判断和运算,然后再写入GPIO配置寄存器

    GPIO_InitTypeDef GPIO_Initstructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	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的初始化已经完成了,这样就可以使用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);

*第三个参数BitVal,这个是根据第三个参数的值来设置指定的端口的
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

*这个函数可以同时对16个端口执行写入操作

首先来使用一下void GPIO_ResetBits,看一下函数的定义

 

 于是可以写为

GPIO_ResetBits(GPIOA, GPIO_Pin_0);

同样可得
 

GPIO_SetBits(GPIOA, GPIO_Pin_0);

接下来使用一下void GPIO_WriteBit

定义中写到,这个端口可以是BitAction枚举中的值,还有Bit_RESET和Bit_SET

GPIO_WriteBit(GPIOA,GPIO_Pin0, Bit_RESET);

 置低电平,灯点亮

GPIO_WriteBit(GPIOA,GPIO_Pin0, Bit_SET);

置高电平,灯熄灭


LED闪烁

为了实现闪烁功能,在逻辑上应该是在循环里,点亮LED,延时一段时间,熄灭LED,延时一段时间

把加入延时函数程序模板放在前面新建工程里了

分别是微秒延时、毫秒延时、秒延时

可以看到是使用SysTick定时器实现的延时

 

#include "stm32f10x.h"                  // Device header
#include "delay.h"

int main(void)
{	
	GPIO_InitTypeDef GPIO_Initstructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_Initstructure);
	
	while(1)
	{	
		GPIO_SetBits(GPIOA, GPIO_Pin_0);
		Delay_ms(500);
		GPIO_ResetBits(GPIOA, GPIO_Pin_0);
		Delay_ms(500);
		
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
		Delay_ms(500);
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
		Delay_ms(500);
		
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);//强制类型转换就可以实现不用填入指定的参数,写0和1即可
		Delay_ms(500);
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
		Delay_ms(500);
	}
}


这里讨论一下推挽输出和开漏输出驱动问题

把LED的正负极对换,这样LED是高电平点亮方式,这样可以看到,LED依旧是正常闪烁的,说明在推挽模式下,高低电平都是有驱动能力的

把端口模式换为GPIO_Mode_Out_OD,开漏输出模式,LED不亮了,现在LED还是高电平点亮方式,LED不亮,说明开漏输出没有高电平驱动能力,再调换LED正负极,可以看到LED正常闪烁,说明开漏模式下低电平是有驱动能力的

这就映证了上节推挽输出和开漏输出的特性,推挽输出高低电平均有驱动能力,开漏输出高电平相当于高阻态,没有驱动能力,低电平有驱动能力

一般输出用推挽输出就行了,开漏输出在特殊情况下使用


LED流水灯

接线图

 

 为什么可以这样操作?将各位转化为二进制是0000000000000001、0000000000000010、0000000000000100,这里每一个端口对应着一个位,按位或的操作就相当于0000000000000111

所有位都为1,相当于选中了所有的引脚

同样解释了why
ResetBits也是这样,看函数定义解释的任意组合(any combination)同样是这个意思

 

 GPIO_Write看第二个参数的定义

 

C语言不支持写二进制,只能在转换为16进制来写,这二进制的16位对应着16个端口

 

#include "stm32f10x.h"                  // Device header
#include "delay.h"

int main(void)
{	
	GPIO_InitTypeDef GPIO_Initstructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_Initstructure.GPIO_Mode = GPIO_Mode_Out_PP;
	//GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; 按或取
	GPIO_Initstructure.GPIO_Pin = GPIO_Pin_All;
	GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_Initstructure);
	
	while(1)
	{	
		//Q1:一开始是没有按位取反~,然后导致小灯常亮
		//Q2:看弹幕说可以用for循环减轻工作量
		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);
		GPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000
		Delay_ms(500);
	}
}


蜂鸣器 

接线图

 

#include "stm32f10x.h"                  // Device header
#include "delay.h"

int main(void)
{	
	GPIO_InitTypeDef GPIO_Initstructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	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);
	
	while(1)
	{	
		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(100);
		GPIO_ResetBits(GPIOB, GPIO_Pin_12);
		Delay_ms(700);
		
	}
}


 库函数的使用方法

 

不过这个用户手册用的版本并不对应我们现在使用库函数的版本

 

*我打不开这个参考文档

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值