文章目录
前言
本系列是本人基于B站up主江协科技的STM32入门教程的学习笔记
一、STM32F103C8T6系统结构
1、 AHB系统总线(先进高性能总线)用于挂载主要的外设,挂载的一般是最基本的或者性能比较高的外设。
2、AHB和APB的总线协议、总线速度、还有数据传送格式的差异 ,所以中间需要通过两个桥接来完成数据的转换和缓存。
3、性能:AHB > APB2 > APB1
系统框图:
二、GPIO
1.GPIO基本结构
1、根据芯片的数据手册可知:GPIO挂载于APB2总线上。
2、寄存器是特殊存储器,内核可以通过APB2总线对寄存器进行读写,且寄存器为32位,只有低 16位对应有端口。
3、驱动器用来增加信号驱动能力。
2.GPIO位结构
1、VDD接3.3V,若输入比3.3V高,则上面的二极管导通,起保护作用。
2、VSS接0V,若输入为负电压,则下面的二极管导通,端口不会从内部电路汲取电流。
3、施密特触发器(TTL)通过两个比较阈值来进行判断,对信号进行整形。
4、模拟输入,接收模拟量;复用功能输入,接收数字量
5、 位设置 / 清除寄存器可以单独操作输出寄存器的某一位,而不影响其它位。
6、若想对输出寄存器的某一位置1,对位设置寄存器的对应位写1;
若想对输出寄存器的某一位置0,对位清除寄存器的对应位写1;
3.GPIO模式
• 浮空输入 ———————GPIO_Mode_IN_FLOATING
• 上拉输入 ————————————GPIO_Mode_IPD
• 下拉输入 ————————————GPIO_Mode_IPU
• 模拟输入 ————————————GPIO_Mode_AIN
• 开漏输出 ————————————GPIO_Mode_Out_OD
• 推挽输出 ————————————GPIO_Mode_Out_PP
• 复用功能的开漏输出 ———————GPIO_Mode_AF_OD
• 复用功能的推挽输出 ———————GPIO_Mode_AF_PP
浮空 / 上拉、下拉输入
1、输入模式下,输出驱动器断开
2、输入通过施密特触发器进行波形整形
3、通过输入寄存器获取IO状态
配置为浮空输入模式时,上下拉电阻都断开;
配置为上拉模式时,上拉电阻连接;
配置为下拉模式时,下拉电阻连接。
模拟输入
1、输出断开,输入施密特触发器关闭
2、ADC模数转换器的专属配置
开漏 / 推挽输出
1、开漏输出的高电平呈现高阻态,无驱动能力,即只有低电平有驱动能力;
推挽输出高低电平均有驱动能力;
2、输出模式下输入模式也有效,一个端口只有一个输出,但可有多个输入
3、开漏输出模式下,P-MOS无效:
数据寄存器为1时,经过反向,N-MOS关闭,输出高阻态;
数据寄存器为0时,经过反向,N-MOS导通,输出接VSS,输出低电平;
该模式下只有低电平有驱动能力,可作为通信协议的驱动方式,在多机通信下可避免各个设备相互干扰。
在外部接5V时,数据寄存器为1时,经过反向,N-MOS关闭,输出5V。
4、推挽输出模式(强推挽输出模式):
数据寄存器为1时,经过反向,P-MOS导通,N-MOS关闭,输出接VDD,输出高电平;
数据寄存器为0时,经过反向,P-MOS关闭,N-MOS导通,输出接VSS,输出低电平;
该模式下高低电平均有较强的驱动能力。
复用开漏/推挽输出
1、引脚电平由片上外设控制的
模式总结
三、GPIO应用
1.GPIO寄存器配置
首先通过RCC的一个寄存器来使能GPIOC的时钟
在APB2外设时钟使能寄存器RCC_APB2ENR里配置IOPCEN,IOPCEN为1,则打开GPIOC的时钟
再配置PC13口的模式,通过端口配置高寄存器GPIOx_CRH(灯接PC13上,低寄存器为0—7,高寄存器为8—15)
最后通过端口输出数据寄存器GPIOx_ODR来决定13口输出的电平
代码如下(示例):
#include "stm32f10x.h" // Device header
//若想做到只配置PC13而不影响其他位,需要 &= 和 |=
int main()
{
RCC->APB2ENR = 0x00000010; //使能GPIOC的时钟
GPIOC->CRH = 0x00300000; //配置PC13口的模式
GPIOC->ODR = 0x00002000; //输出高电平,LED灭
GPIOC->ODR = 0x00000000; //输出低电平,LED亮
while(1)
{
}
}
2.库函数
常用RCC库函数:
//参数通过跳转至函数介绍了解
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
//AHB外设时钟控制函数:使能或失能AHB外设时钟
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
//APB2外设时钟控制函数:使能或失能APB2外设时钟
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
//APB1外设时钟控制函数:使能或失能APB1外设时钟
常用GPIO库函数:
//参数通过跳转至函数介绍了解
void GPIO_DeInit(GPIO_TypeDef* GPIOx);
//复位所指定的GPIO外设
void GPIO_AFIODeInit(void);
//复位所指定的AFIO外设
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//用结构体的参数来初始化GPIO口
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);
//读取整个输出数据寄存器的输出值
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);
//同时对16个端口进行写入操作
3.GPIO输出
代码如下(示例):
#include "stm32f10x.h" // Device header
int main()
{
GPIO_InitTypeDef GPIO_InitStructure;
//给结构体取个名字(配置端口模式时需要结构体)
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC,ENABLE);
//开启时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//配置PC13口的模式
GPIO_Init (GPIOC,&GPIO_InitStructure);
//初始化GPIO口
GPIO_SetBits (GPIOC ,GPIO_Pin_13); //把指定端口设置为高电平
// GPIO_ResetBits (GPIOC ,GPIO_Pin_13); //把指定端口设置为低电平
while(1)
{
}
}
4.GPIO输入
接线图:
代码如下(示例):
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum;
int main()
{
LED_Init ();
Key_Init ();
while(1)
{
KeyNum = Key_GetNum ();
if(KeyNum == 1)
{
LED1_Turn();
}
if(KeyNum == 2)
{
LED2_Turn ();
}
}
}
Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
LED.c
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStrcture;
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE ); //GPIOA时钟使能
GPIO_InitStrcture.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStrcture.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2 ; //PA1&PA2
GPIO_InitStrcture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init (GPIOA ,&GPIO_InitStrcture);
GPIO_SetBits (GPIOA ,GPIO_Pin_1 |GPIO_Pin_2 );
}
void LED1_ON(void)
{
GPIO_ResetBits (GPIOA ,GPIO_Pin_1 );
}
void LED1_OFF(void)
{
GPIO_SetBits (GPIOA ,GPIO_Pin_1 );
}
void LED2_ON(void)
{
GPIO_ResetBits (GPIOA ,GPIO_Pin_2 );
}
void LED2_OFF(void)
{
GPIO_SetBits (GPIOA ,GPIO_Pin_2 );
}
void LED1_Turn(void)
{
//GPIO_Pin_1 = 0时灯为点亮状态,低电平点亮,led外接了高电平
if(GPIO_ReadInputDataBit (GPIOA ,GPIO_Pin_1 ) == 0)
{
GPIO_SetBits (GPIOA ,GPIO_Pin_1 );
}
else
{
GPIO_ResetBits (GPIOA ,GPIO_Pin_1 );
}
}
void LED2_Turn(void)
{
if(GPIO_ReadInputDataBit (GPIOA ,GPIO_Pin_2 ) == 0)
{
LED2_OFF ();
}
else
{
LED2_ON ();
}
}
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB ,ENABLE );
GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue .GPIO_Mode = GPIO_Mode_IPU ;
GPIO_InitStructrue .GPIO_Pin = GPIO_Pin_11 |GPIO_Pin_1 ;
GPIO_InitStructrue .GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init (GPIOB ,&GPIO_InitStructrue );
}
unsigned char Key_GetNum(void)
{
uint8_t Key_Num = 0;
if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_1 ) == 0)
{
Delay_ms (20);
while(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_1 ) == 0);
Delay_ms (20);
Key_Num = 1;
}
if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_11 ) == 0)
{
Delay_ms (20);
while(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_11 ) == 0);
Delay_ms (20);
Key_Num = 2;
}
return Key_Num ;
}