Fork me on GitHub

STM32笔记1

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);
}

Comments