嵌入式学习--江协stm32day1
失踪人口回归了,stm32的学习比起51要慢一些,因为涉及插线,可能存在漏插,不牢固等问题。
相对于51直接对寄存器的设置,stm32因为是32位修改起来比较麻烦,江协课程是基于标准库的,是对封装函数进行操作,这要求我们对于模块的使用在开始就规划好。
GPIO通用输入输出口
APB2是外设总线
输入模式
- 浮空输入
- 原理:GPIO 端口无内部上拉或下拉电阻,电平状态完全由外部输入决定,引脚悬空时电平不确定。
- 使用场景:用于串口通信接收端(如 UART、USART 的 RX 引脚 ),接收外部设备电平变化信号;检测有稳定高低电平的外部传感器信号(如霍尔、红外传感器 );外部中断信号输入检测;数字输入信号检测。
- 特点:能真实反映外部电平,但易受干扰,引脚悬空时读数无参考意义。
- 上拉输入
- 原理:内部连接上拉电阻,无外部输入信号时,GPIO 端口保持高电平。
- 使用场景:机械按键或拨动开关输入,未按下时为高电平,按下接地变低电平;IIC 通信 SDA 引脚,保证总线数据传输默认高电平;SPI 通信从设备选择引脚(NSS),无信号时保持高,主设备选择时拉低 ;继电器状态等开关量信号输入检测;电源检测引脚,检测电源供电状态。
- 特点:确保无外部信号时输入为高电平,增强信号稳定性,防信号漂移。
- 下拉输入
- 原理:内部连接下拉电阻,无外部输入信号时,GPIO 端口保持低电平。
- 使用场景:CAN_RX 引脚接收 CAN 总线信号,确保总线无信号时引脚低电平;按钮接地触发的按键输入,未按下时低电平,按下拉高 ;默认低电平的数字电路信号输入;下拉电阻保持低电平的光电开关等传感器输入;检测外部设备低电平状态的电路。
- 特点:确保无外部信号时输入为低电平,适用于外部信号常态为高的检测场景。
- 模拟输入
- 原理:输入信号不经施密特触发器处理,直接接入内部 ADC,将模拟信号转为数字信号。
- 使用场景:连接温度、光照、湿度、气压等模拟传感器采集信号;电池电压检测;电流检测(通过分流电阻和运算放大器转换为电压信号 );光强检测等。
- 特点:用于采集连续变化的模拟量,供 MCU 处理分析。
输出模式
- 推挽输出
- 原理:由两个互补晶体管组成,可输出高电平(接 VDD )和低电平(接 VSS ),能向负载灌电流或抽取电流,导通损耗小、效率高。
- 使用场景:驱动 LED、继电器、蜂鸣器;控制小型直流电机;SPI 通信的 SCK、MOSI、MISO 等需强电平信号的总线通信引脚;各类状态指示灯控制。
- 特点:驱动能力强,可快速切换高低电平,适合直接驱动数字负载。
- 开漏输出
- 原理:输出端类似三极管集电极,只能输出低电平(接 VSS ),输出高电平时为高阻态,需外部上拉电阻拉高。
- 使用场景:IIC 总线通信的 SCL 和 SDA 引脚;多设备共享数据线的通信总线;GPIO 中断信号输出(通过外部上拉电阻共享中断信号线 );不同电压域电源管理切换电路;电平转换(适配不同电压器件 )。
- 特点:可实现线与逻辑,方便电平匹配,适合多设备通信及跨电压域应用,但高电平需外部上拉。
- 复用推挽输出
- 原理:GPIO 端口由片上外设控制,如定时器 PWM 输出、SPI 的 MOSI 和 MISO 等,兼具推挽输出特性,能主动提供电流驱动负载。
- 使用场景:UART 通信发送端(TX 引脚 );SPI 通信的时钟线(SCK)、主输出从输入(MOSI)、主输入从输出(MISO)引脚 ;CAN 通信发送端(TX 引脚 );伺服电机或 DC 电机控制的 PWM 信号输出;外部设备的时钟、使能等控制信号输出。
- 特点:用于特定外设功能,借助推挽输出特性提供稳定驱动。
- 复用开漏输出
- 原理:GPIO 端口由片上外设控制,输出模式为开漏输出,高电平需外部或内部上拉电阻,可实现线与逻辑。
- 使用场景:IIC 通信的 SDA 和 SCL 引脚(多设备共享 );SMBus 通信(类似 IIC 协议 );1 - Wire 单总线通信;MCU 接收多个外设中断信号(避免电平冲突 );电源管理信号(控制电压域转换 )。
- 特点:适用于特定外设多设备共享总线通信,需外部上拉电阻配合,可解决电平冲突问题。
这里只需要大概了解一下即可,后面结合具体外设理解
GPIO在后面会频繁使用,我们要熟悉使用流程
1.配置时钟2.初始化结构体
GPIO输出
以LED介绍GPIO的使用
#include "stm32f10x.h" // Device header
void LED_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);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);GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
void LED1_ON()
{GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF()
{GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn()
{if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0){GPIO_SetBits(GPIOA,GPIO_Pin_1);}else{GPIO_ResetBits(GPIOA,GPIO_Pin_1);}
}
LED闪烁
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟//使用各个外设前必须开启时钟,否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第0号引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOA的初始化/*主循环,循环体内的代码会一直循环执行*/while (1){/*设置PA0引脚的高低电平,实现LED闪烁,下面展示3种方法*//*方法1:GPIO_ResetBits设置低电平,GPIO_SetBits设置高电平*/GPIO_ResetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为低电平Delay_ms(500); //延时500msGPIO_SetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为高电平Delay_ms(500); //延时500ms/*方法2:GPIO_WriteBit设置低/高电平,由Bit_RESET/Bit_SET指定*/GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); //将PA0引脚设置为低电平Delay_ms(500); //延时500msGPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); //将PA0引脚设置为高电平Delay_ms(500); //延时500ms/*方法3:GPIO_WriteBit设置低/高电平,由数据0/1指定,数据需要强转为BitAction类型*/GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0); //将PA0引脚设置为低电平Delay_ms(500); //延时500msGPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1); //将PA0引脚设置为高电平Delay_ms(500); //延时500ms}
}
LED流水灯
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟//使用各个外设前必须开启时钟,否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //GPIO引脚,赋值为所有引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOA的初始化/*主循环,循环体内的代码会一直循环执行*/while (1){/*使用GPIO_Write,同时设置GPIOA所有引脚的高低电平,实现LED流水灯*/GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001,PA0引脚为低电平,其他引脚均为高电平,注意数据有按位取反Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010,PA1引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100,PA2引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000,PA3引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000,PA4引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000,PA5引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000,PA6引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100msGPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000,PA7引脚为低电平,其他引脚均为高电平Delay_ms(100); //延时100ms}
}
蜂鸣器
这个是有源蜂鸣器 ,没法唱天空之城,差评
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟//使用各个外设前必须开启时钟,否则对外设的操作无效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIO引脚,赋值为第12号引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数//函数内部会自动根据结构体的参数配置相应寄存器//实现GPIOB的初始化/*主循环,循环体内的代码会一直循环执行*/while (1){GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止Delay_ms(100); //延时100msGPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫Delay_ms(100); //延时100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止Delay_ms(700); //延时700ms}
}
GPIO输入
按键控制LED
IPU为上拉输入(这里还没学定时器,还是用delay消抖)
#include "stm32f10x.h" // Device header
#include "Delay.h"/*** 函 数:按键初始化* 参 数:无* 返 回 值:无*/
void Key_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟/*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); //将PB1和PB11引脚初始化为上拉输入
}/*** 函 数:按键获取键码* 参 数:无* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手*/
uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0; //定义变量,默认键码值为0if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下{Delay_ms(20); //延时消抖while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手Delay_ms(20); //延时消抖KeyNum = 1; //置键码为1}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下{Delay_ms(20); //延时消抖while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手Delay_ms(20); //延时消抖KeyNum = 2; //置键码为2}return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
光敏电阻控制蜂鸣器
也是化身电报专家了
#include "stm32f10x.h" // Device header/*** 函 数:光敏传感器初始化* 参 数:无* 返 回 值:无*/
void LightSensor_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟/*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); //将PB13引脚初始化为上拉输入
}/*** 函 数:获取当前光敏传感器输出的高低电平* 参 数:无* 返 回 值:光敏传感器输出的高低电平,范围:0/1*/
uint8_t LightSensor_Get(void)
{return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13); //返回PB13输入寄存器的状态
}
学习stm32过程中,感觉自己真的要成为工程师了,cv工程师(bushi)。在使用外设前,要先去函数库找到需要使用的函数,然后一个个复制过去,然后再去查看函数的定义,配置里面的参数,需要逻辑的大多都是在主函数里面写(也有可能是入门学习的原因)
今日语录:打不倒我的,只会让我更加强大(农p无疑了)