STM32系列文章01:流水灯
接触单片机的第一个实验就是跑马灯实验,虽说这个实验比较简单,但万事开头难。闲话少说,直接上代码:
跑马灯实验的实现大致可以分为三个步骤:
1. LED初始化
LED初始化包括时钟初始化和引脚初始化。每一个外设都有其对应的时钟,所以在使用外设之前都要先开启外设的时钟。
引脚初始化(端口初始化),本实验是要通过引脚输出高低电平来控制LED的亮灭。端口初始化主要是配置:输出引脚、输出模式、输出速率。
时钟及端口初始化代码如下:
/*本次跑马灯实验主要用到PB6~PB9几个端口*/ #include "LED.h" void LED_Init(void) //自定义Led灯初始化函数 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA, ENABLE); /*使能LED灯使用的GPIO时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //以上为配置GPIO GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //以上为配置GPIO GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //以上为配置GPIO GPIO_Init(GPIOD, &GPIO_InitStructure); //初始化GPIOD }
附上头文件:
/*头文件中主要是一些宏定义和位带操作*/ #ifndef __LED_H #define __LED_H #include "stm32f10x.h" #define LED1_ON GPIO_SetBits(GPIOB, GPIO_Pin_6) #define LED1_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_6) #define LED2_ON GPIO_SetBits(GPIOB, GPIO_Pin_7) #define LED2_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_7) #define LED3_ON GPIO_SetBits(GPIOB, GPIO_Pin_8) #define LED3_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_8) #define LED4_ON GPIO_SetBits(GPIOB, GPIO_Pin_9) #define LED4_OFF GPIO_ResetBits(GPIOB, GPIO_Pin_9) #define LED5_ON GPIO_SetBits(GPIOA, GPIO_Pin_0) #define LED5_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_0) #define LED6_ON GPIO_SetBits(GPIOA, GPIO_Pin_1) #define LED6_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_1) #define LED7_ON GPIO_SetBits(GPIOA, GPIO_Pin_2) #define LED7_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_2) #define LED8_ON GPIO_SetBits(GPIOA, GPIO_Pin_3) #define LED8_OFF GPIO_ResetBits(GPIOA, GPIO_Pin_3) #define LED9_ON GPIO_SetBits(GPIOC, GPIO_Pin_3) #define LED9_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_3) #define LED10_ON GPIO_SetBits(GPIOC, GPIO_Pin_2) #define LED10_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_2) #define LED11_ON GPIO_SetBits(GPIOC, GPIO_Pin_1) #define LED11_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_1) #define LED12_ON GPIO_SetBits(GPIOC, GPIO_Pin_0) #define LED12_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_0) #define LED13_ON GPIO_SetBits(GPIOC, GPIO_Pin_10) #define LED13_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_10) #define LED14_ON GPIO_SetBits(GPIOC, GPIO_Pin_11) #define LED14_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_11) #define LED15_ON GPIO_SetBits(GPIOC, GPIO_Pin_12) #define LED15_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_12) #define LED16_ON GPIO_SetBits(GPIOC, GPIO_Pin_13) #define LED16_OFF GPIO_ResetBits(GPIOC, GPIO_Pin_13) //位带操作,实现51类似的GPIO控制功能 //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页). //IO口操作宏定义 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08 //IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入 #define LED1 PBout(6) #define LED2 PBout(7) #define LED3 PBout(8) #define LED4 PBout(9) #define LED5 PAout(0) #define LED6 PAout(1) #define LED7 PAout(2) #define LED8 PAout(3) #define LED9 PCout(3) #define LED10 PCout(2) #define LED11 PCout(1) #define LED12 PCout(0) #define LED13 PCout(10) #define LED14 PCout(11) #define LED15 PCout(12) #define LED16 PCout(13) void LED_Init(void); #endif
2.延时函数的编写
跑马灯实验中需要用到延时函数,这个网上有很多例子。下面给出一个比较常用且精准的延时函数。感兴趣的朋友也可以自己去编写属于自己的延时函数。这个函数比较常用,在后面的实验中也会经常用到。
源文件:
#include "delay.h" void delay_us(u32 nus) { u32 temp; SysTick->LOAD = 9*nus; SysTick->VAL=0X00;//清空计数器 SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源 do { temp=SysTick->CTRL;//读取当前倒计数值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } void delay_ms(u16 nms) { u32 temp; SysTick->LOAD = 9000*nms; SysTick->VAL=0X00;//清空计数器 SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源 do { temp=SysTick->CTRL;//读取当前倒计数值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达 SysTick->CTRL=0x00; //关闭计数器 SysTick->VAL =0X00; //清空计数器 }
头文件:
#ifndef __DELAY_H #define __DELAY_H #include "stm32f10x.h" void delay_us(u32 nus); void delay_ms(u16 nms); #endif
主函数就相对比较简单了,调用LED的初始化函数,依次点亮LED。本实验中使用的是位带操作,也可以用上面提供的第一种方式操作,当然网上也有其他的如直接操作寄存器的,都是可以的,因人而异。
/*引用头文件是为了调用其中定义的函数*/ #include "stm32f10x.h" #include "delay.h" #include "led.h" int main(void) { LED_Init(); while(1) { LED1 = 1; delay_ms(200); LED1 = 0; delay_ms(200); LED2 = 1; delay_ms(200); LED2 = 0; delay_ms(200); LED3 = 1; delay_ms(200); LED3 = 0; delay_ms(200); LED4 = 1; delay_ms(200); LED4 = 0; delay_ms(200); } }
至此,跑马灯实验就结束了。本实验仅供有需要的朋友参考,切记不可照抄照搬,希望在看的时候加入一些自己的思考,根据自己的原理图来做相应的修改。
参考资料:
STM32中文参考手册_V10
Cortex-M3权威指南(中文)
资料下载地址:https://pan.baidu.com/s/16W3K2zkJJBOaxYb4jJctgg
提取码:wwkn
完整工程下载链接:https://pan.baidu.com/s/109sOF0eroGE0bHeTTM1Fww
写在最后:
如果上面的下载链接失效了,可以私信我给你们提供资料。
如果你喜欢我的文章,可以随意打赏我,以激励我继续写下去。谢谢!