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定时器的操作步骤:

  1. 设置systick定时器的时钟源
  2. 设置systick定时器的重装初始值(若使用中断,就将中断使能打开)
  3. 清零systick定时器计数器的值
  4. 打开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作为时钟源的例子:

  1. 复位RCC时钟
  2. 打开HSE外部高速时钟
  3. 监测HSE外部高速时钟是否开启成功
  4. 设置FLASH读写
  5. 设置AHB总线的分频,还有APB1和APB2的分频,:AHB和APB2最大频率72MHz,APB1做大频率36MHz
  6. 设置HSE外部高速时钟作为PLLs时钟的时钟输入
  7. 设置PLL时钟的倍频倍数
  8. 打开PLL时钟的使能
  9. 等待PLL时钟的开启成功
  10. 将系统时钟源设置为PLL时
  11. 等待时钟源切换成功
//在上一个实验基础上
//自定义系统时钟
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);
}