STM32F103学习笔记
GPIO初始化和读写操作
STM32的GPIO引脚有多种模式使用,在使用前需要进行配置。
LED灯实验
#include "stm32f10x.h"
#define LED GPIO_Pin_All
void delay(u32 i)
{
while(i--);
}
//LED的GPIO初始化程序
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIO
}
void led_display()
{
GPIO_SetBits(GPIOC,LED);
delay(6000000);
GPIO_ResetBits(GPIOC,LED);
delay(6000000);
}
int main()
{
LED_Init();
while(1)
{
led_display();
}
}
蜂鸣器实验
使用无源蜂鸣器
#include "stm32f10x.h"
#define BZ GPIO_Pin_5 //PB5 定义端口PB5
void delay(u32 i)
{
while(i--);
}
void BEEP_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = BZ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIO
}
void sound(u32 i) // i= 5000救护车,i=1000电动车
{
while(i--)
{
GPIO_SetBits(GPIOB,BZ);
delay(i);
GPIO_ResetBits(GPIOB,BZ);
delay(i);
}
}
int main()
{
BEEP_Init(); //端口初始化
while(1)
{
sound(5000);
}
}
SysTick实验
系统滴答计时器比延时函数更加精确,可移植性更高。systick定时器是24位的定时器,当定时器计数到0时,将自动从RELOAD寄存器中重装定时器初值,如果开启了中断,此时还会产生异常中断信号。
定时器必须要一个时钟来驱动,systick定时器的时钟来源时系统时钟,不过它的时钟可以选择成直接取自系统时钟,也可以将系统时钟8分频后再赋给systick定时器。
systick定时器的操作步骤:
- 设置systick定时器的时钟源
- 设置systick定时器的重装初始值(若使用中断,就将中断使能打开)
- 清零systick定时器计数器的值
- 打开systick定时器
#include "stm32f10x.h"
#define LED GPIO_Pin_All
void delay_us(u32 i)
{
u32 temp;
SysTick->LOAD = 9*i; //设置重装数值 72MHz时
SysTick->CTRL = 0x01; //使能,减到零时无动作,采用外部时钟源(8分频系统时钟)
SysTick->VAL = 0; //清零计数器
do
{
temp = SysTick->CTRL; //读取当前计数值
}
while((temp & 0x01) && (!(temp &(1<<16))));
//与运算取出指定位的数值
//实际上就是CTRL寄存器的第1位为0或第16位为1时,退出循环
SysTick->CTRL=0; //关闭计数器
SysTick->VAL = 0; //清空计数器
}
void delay_ms(u32 i)
{
u32 temp;
SysTick->LOAD=9000*i; //设置重装数值, 72MHZ时
SysTick->CTRL=0x01; //使能,减到零是无动作,采用外部时钟源
SysTick->VAL=0; //清零计数器
do
{
temp=SysTick->CTRL; //读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16)))); //等待时间到达
SysTick->CTRL=0; //关闭计数器
SysTick->VAL=0; //清空计数器
}
//LED的GPIO初始化程序
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIO
}
int main()
{
u8 i;
LED_Init();
while(1)
{
for(i=0;i<8;i++)
{
GPIO_Write(GPIOC,(u16)~(0x01<<i));
delay_ms(1000); //精确延时1秒
}
}
}
系统时钟实验
STM32一共可以有4个时钟源
- HSI(High Speed Inner)内部自带的高速时钟,单片机启动后默认使用的时钟源
- HSE (High Speed External)外部高速时钟,大多数时8M晶振
- LSE(Low Speed External)外部低速时钟,给单片机内部RTC提供时钟
- LSI,内部低速时钟,主要给单片机内部RTC和看门狗提供时钟
STM32的系统时钟源,有3个时钟来源:
- 直接来自内部高速时钟HSI
- 直接来自外部的高速时钟HSE
- 将HSI或HSE进行处理,倍频之后的PLL时钟(Phase-Locked Loops锁相环)
STM32设置RCC(复位和时钟控制)时钟的步骤
以设置外部高速时钟作为PLL输入,然后以PLL作为时钟源的例子:
- 复位RCC时钟
- 打开HSE外部高速时钟
- 监测HSE外部高速时钟是否开启成功
- 设置FLASH读写
- 设置AHB总线的分频,还有APB1和APB2的分频,注:AHB和APB2最大频率72MHz,APB1做大频率36MHz
- 设置HSE外部高速时钟作为PLLs时钟的时钟输入
- 设置PLL时钟的倍频倍数
- 打开PLL时钟的使能
- 等待PLL时钟的开启成功
- 将系统时钟源设置为PLL时
- 等待时钟源切换成功
//在上一个实验基础上
//自定义系统时钟
void RCC_HSE_Configuration()
{
RCC_DeInit(); //重置RCC外设寄存器
RCC_HSEConfig(RCC_HSE_ON); //设置外部高速晶振(HSE)
if(RCC_WaitForHSEStartUp() == SUCCESS) //等待HSE起振
{
RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置AHB时钟
RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1)
RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速AHB时钟(PCLK2)
RCC_PLLConfig(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);//设置PLL时钟源及倍频系数,实际系统时钟36MHz
RCC_PLLCmd(ENABLE); //PLL使能
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //设置系统时钟(SYSCLK)
while(RCC_GetSYSCLKSource()网易 != 0x08); //返回用作系统时钟的时钟源,0x08,PLL作为系统时钟
}
}
int main()
{
LED_Init();
RCC_HSE_Configuration(); //自定义系统时钟,修改倍频或分频参数
while(1)
{
GPIO_SetBits(GPIOC,LED);
delay_ms(500); //精确延时0.5s,实际延时1s
GPIO_ResetBits(GPIOC,LED);
delay_ms(500);
}
}
按键实验
注意按键5ms-10ms左右的延时消抖,注意按键的上拉还是下拉
#include "stm32f10x.h"
#define K_UP GPIO_Pin_0 //PA0
#define K_DOWN GPIO_Pin_3 //PE3
#define K_LEFT GPIO_Pin_2 //PE2
#define K_RIGHT GPIO_Pin_4 //PE4
#define k_up GPIO_ReadInputDataBit(GPIOA,K_UP) //获取按键的状态
#define k_down GPIO_ReadInputDataBit(GPIOE,K_DOWN)
#define k_left GPIO_ReadInputDataBit(GPIOE,K_LEFT)
#define k_right GPIO_ReadInputDataBit(GPIOE,K_RIGHT)
#define LED GPIO_Pin_All
void delay_ms(u32 i)
{
u32 temp;
SysTick->LOAD=9000*i; //设置重装数值, 72MHZ时
SysTick->CTRL=0x01; //使能,减到零是无动作,采用外部时钟源
SysTick->VAL=0; //清零计数器
do
{
temp=SysTick->CTRL; //读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16)))); //等待时间到达
SysTick->CTRL=0; //关闭计数器
SysTick->VAL=0; //清空计数器
}
//LED的GPIO初始化程序
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIO
}
void key_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SystemInit();
//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=K_UP; //选择你要设置的IO口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//下拉输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //设置传输速率
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
GPIO_InitStructure.GPIO_Pin=K_DOWN|K_LEFT|K_RIGHT;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,K_UP); //对K_UP初始化输出0
}
void key_pros() //按键处理函数
{
if(k_up==1) //判断按键k_up是否按下
{
delay_ms(10); //消抖处理
if(k_up==1) //再次判断按键k_up是否按下
{
GPIO_Write(GPIOC,(u16)0xfe);
}
while(k_up); //等待按键松开
}
if(k_down==0)
{
delay_ms(10);
if(k_down==0)
{
GPIO_Write(GPIOC,(u16)(0xfd));
}
while(!k_down);
}
if(k_left==0)
{
delay_ms(10);
if(k_left==0)
{
GPIO_Write(GPIOC,(u16)(0xfb));
}
while(!k_left);
}
if(k_right==0)
{
delay_ms(10);
if(k_right==0)
{
GPIO_Write(GPIOC,(u16)(0xf7));
}
while(!k_right);
}
}
int main()
{
LED_Init(); //LED初始化
key_init(); //按键端口初始化函数
GPIO_Write(GPIOC,(u16)(0xff));
while(1)
{
key_pros(); //按键处理函数
}
}
数码管实验
#include "stm32f10x.h"
#define smg_duan (GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7)//PC0~PC7
u8 smgduan[16]={0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71};//0~F 数码管段选数据
void delay_ms(u32 i)
{
u32 temp;
SysTick->LOAD=9000*i; //设置重装数值, 72MHZ时
SysTick->CTRL=0x01; //使能,减到零是无动作,采用外部时钟源
SysTick->VAL=0; //清零计数器
do
{
temp=SysTick->CTRL; //读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16)))); //等待时间到达
SysTick->CTRL=0; //关闭计数器
SysTick->VAL=0; //清空计数器
}
void smg_init()
{
GPIO_InitTypeDef GPIO_InitStructure; //声明一个结构体变量,用来初始化GPIO
/* 开启GPIO时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=smg_duan; //选择你要设置的IO口
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); /* 初始化GPIO */
}
void static_smg_display() //静态数码管显示
{
u8 i;
for(i=0;i<16;i++)
{
GPIO_Write(GPIOC,(u16)(~smgduan[i]));
delay_ms(1000);
}
}
int main()
{
smg_init(); //数码管端口初始化函数
while(1)
{
static_smg_display(); //静态数码管显示
}
}
中断和定时器
外部中断实验
注意将端口引脚映射到外部中断线路上,注意配置中断优先级
#include "stm32f10x.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#define k_left GPIO_Pin_2
#define LED GPIO_Pin_All
void delay_ms(u32 i)
{
u32 temp;
SysTick->LOAD=9000*i; //设置重装数值, 72MHZ时
SysTick->CTRL=0x01; //使能,减到零是无动作,采用外部时钟源
SysTick->VAL=0; //清零计数器
do
{
temp=SysTick->CTRL; //读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16)))); //等待时间到达
SysTick->CTRL=0; //关闭计数器
SysTick->VAL=0; //清空计数器
}
//LED的GPIO初始化程序
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOC,LED);
}
void exti_init(void) //外部中断初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SystemInit();
//开启GPIO时钟,用到了时引脚复用功能,开启复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitStructure.GPIO_Pin = k_left;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
//选择外部中断线路对应的GPIO管脚,此处是PE2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
//设置外部中断的模式
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
//设置NVIC参数(nested vector interrupt config)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //打开EXTI2的全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
}
void EXTI2_IRQHandler(void) //外部中断2中断处理函数
{
if(EXTI_GetITStatus(EXTI_Line2)==SET)
{
EXTI_ClearITPendingBit(EXTI_Line2);//清除EXTI2线路挂起位
delay_ms(10);//消抖
if(GPIO_ReadInputDataBit(GPIOE,k_left)==Bit_RESET) //k_left按下
{
delay_ms(10);//消抖
if(GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_0)==Bit_RESET)
{
//LED熄灭
GPIO_SetBits(GPIOC,GPIO_Pin_0);
}
else
{
//LED发光
GPIO_ResetBits(GPIOC,GPIO_Pin_0);
}
}
while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0); //等待按键松开
}
}
int main()
{
led_init();
exti_init();
while(1);
}
定时器实验
STM32中一共有11个定时器:
- 2个高级控制定时器 (TIM1,TIM8)
- 4个通用定时器 (TIM2-TIM5)
- 2个基本定时器 (TIM6,TIm7)
- 2个看门狗定时器
- 1个系统滴答定时器
注:TIM2-TIM7的时钟由APB1产生,TIM1和TIM8是由APB2产生时钟
//实现1s流水灯实验
#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#define LED GPIO_Pin_All
//LED的GPIO初始化程序
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO时钟初始化
SystemInit();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//配置GPIO模式和端口
GPIO_InitStructure.GPIO_Pin = LED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化GPIO
}
void time_init()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定时器初始化结构体
NVIC_InitTypeDef NVIC_InitStructure;
//开启定时器3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIMx的中断待处理位:TIM中断源
TIM_TimeBaseInitStructure.TIM_Period = 2000;//设置自动重装寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 36000-1;//设置作为TIMx时钟频率的预分频值,2Khz计数频率
TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
TIM_Cmd(TIM3,ENABLE); //使能或失能TIMx外设
//设置中断参数,并打开中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //使能或失能指定的TIM中断
//设置NVIC参数
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //打开EXTI2的全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
}
void TIM3_IRQHandler() //定时器3的中断处理函数
{
static u8 i = 0;
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
GPIO_Write(GPIOC,(u16)~(0x01<<i++));
if(i==8)i=0;
}
int main()
{
time_init();
led_init();
while(1);
}