以 STM32点亮流水灯

、以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED 搭建电路,使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)分别用汇编语言,C语言编程实现。

一、工程的建立

1、新建相关文件

新建总文件夹,用来存放本次工程的所有程序,然后再建CORE、HARDWARE、OBJ、FWLIB、SYSTEM、USER这六个文件夹。其中,HARDWARE文件夹是用来存放外设硬件代码,OBJ用来存放生成调试代码,FWLIB是各种.c和.h文件
在这里插入图片描述

2、打开MDK,建立新工程,保存到USER下:

芯片型号选择STM32F103C8,然后会弹出run-time environment窗口,选择取消
USER文件夹会出现这两个我们需要的文件
在这里插入图片描述

3、 在MDK添加项目所需要的分组以及文件:

在这里插入图片描述

4、 配置options for target:

在这里插入图片描述
其中,select folder for objects是选择生成的hex存放的目录,这里选择存放在OBJ文件夹中,create HEX file是生成hex文件,用于下载到开发板的:
在这里插入图片描述
以上只是参照流程配置过程截图,并不完整。

具体流程

5、 添加LED驱动代码:

(一)配置GPIO端口

时钟配置:
stm32提供了一个用c语言封装好的固件库调用相应的库函数。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //打开外设GPIOB的时钟

GPIO初始化结构体
配置GPIO端口的输入输出模式设置 , 最大速率设置等

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

配置为通用推挽输出、输出速度为2M

    GPIO_InitTypeDef   GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1 ;             //选定端口为GPIO_Pin_1
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);

我们需要的是输出高低电平,所以要设置为输出。输出模式又有好几种输出:
  推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
  开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。

至此一个GPIOB_Pin_1配置完毕。
在这里插入图片描述
led.h

#ifndef _LED_H
#define _LED_H

#include "stm32f10x.h"
void LED_R_TOGGLE(void);
void LED_G_TOGGLE(void);
void LED_Y_TOGGLE(void);
void LED_Init(void);
#endif

led.c

 #include "led.h"
#include "delay.h"

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);  //打开外设GPIOA、GPIOB、GPIOC的时钟
	
	GPIO_InitTypeDef   GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12 ;             //选定端口为GPIOA_Pin_12
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1 ;             //选定端口为GPIOB_Pin_1
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//输出模式为通用推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14 ;             //选定端口为GPIOC_Pin_14
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//输出速度为2M
	
	GPIO_Init(GPIOC,&GPIO_InitStruct);
}

void LED_R_TOGGLE(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pin_12);
	delay_ms(500);
	GPIO_ResetBits(GPIOA,GPIO_Pin_12);
	
}
void LED_G_TOGGLE(void)
{
	GPIO_SetBits(GPIOB, GPIO_Pin_1);
	delay_ms(500);
	GPIO_ResetBits(GPIOB,GPIO_Pin_1);
	
}
void LED_Y_TOGGLE(void)
{
	GPIO_SetBits(GPIOC, GPIO_Pin_14);	
	delay_ms(500);
	GPIO_ResetBits(GPIOC,GPIO_Pin_14);
}

(二)主函数

main.c

#include  "stm32f10x.h"
#include "delay.h"
#include "led.h"
int main(void)
{			  
	
	LED_Init();	     //LED初始化
	delay_init();	   //延时初始化                     
	
	while(1)
	{
		LED_R_TOGGLE();    //红灯闪烁
		
		delay_ms(500);
		LED_G_TOGGLE();    //绿灯闪烁
		delay_ms(500);
		LED_Y_TOGGLE();    //黄灯闪烁
		delay_ms(500);
	}

其他函数
GPIO端口配置完成之后,如果需要确定闪烁时间,我们就需要添加延时函数,这部分系统中断,这里不做详细介绍。

二、各寄存器地址和详细参数

提到单片机,就不得不提到寄存器。根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。

1、寄存器地址的查找

假如我们想读取PB3引脚的电平

第一步,找到GPIOB的基地址
所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围
在这里插入图片描述

第二步,找到端口输入寄存器的地址偏移
找到存储数据的那个屋子,结论是0x4001 0C00+8 = 0x4001 0C08
在这里插入图片描述

第三步,找到知道数据的那个人
PB3的数据位于从右往左数第4个。
而这个寄存器的位数是32位(虽然高16位没有用到),这就是32位的单片机的意思。每个寄存器都占据4字节,32位。而CPU的总线一次可以操作32位。
结论:PB3的输入数据位于0x4001 0C08这个地址上,这个地址上存放数据的右起第4个位就是PB3引脚对应的高低电平。
直接访问这个地址:GPIOB端口的起始地址+偏移地址

unsigned int *pGPIOB_IDR = (unsigned int *)0x40010C08;
 unsigned char PB3 = *pGPIOB_IDR & 0x8;//取出从右往左数的第4位

写入其他寄存器地址

/*RCC外设基地址*/
#define  RCC_BASE             (unsigned int)(0x40021000)
/*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/
#define  RCC_APB2ENR		 *(unsigned int*)(RCC_BASE+0x18)

/*GPIOB外设基地址*/
#define  GPIOB_BASE           (unsigned int)(0X40010C00)

/* GPIOB寄存器地址,强制转换成指针 */
#define GPIOB_CRL			*(unsigned int*)(GPIOB_BASE+0x00)
#define GPIOB_CRH			*(unsigned int*)(GPIOB_BASE+0x04)
#define GPIOB_IDR			*(unsigned int*)(GPIOB_BASE+0x08)
#define GPIOB_ODR			*(unsigned int*)(GPIOB_BASE+0x0C)
#define GPIOB_BSRR	        *(unsigned int*)(GPIOB_BASE+0x10)
#define GPIOB_BRR			*(unsigned int*)(GPIOB_BASE+0x14)
#define GPIOB_LCKR		    *(unsigned int*)(GPIOB_BASE+0x18)

/*GPIOC外设基地址*/
#define GPIOC_BASE           (unsigned int)(0x40011000)

/* GPIOC寄存器地址,强制转换成指针 */
#define GPIOC_CRL			*(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH			*(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR			*(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR			*(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR	        *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR			*(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR		    *(unsigned int*)(GPIOC_BASE+0x18)

	
/*GPIOC外设基地址*/
#define GPIOD_BASE            (unsigned int)(0x40011400)

/* GPIOC寄存器地址,强制转换成指针 */
#define GPIOD_CRL			*(unsigned int*)(GPIOD_BASE+0x00)
#define GPIOD_CRH			*(unsigned int*)(GPIOD_BASE+0x04)
#define GPIOD_IDR			*(unsigned int*)(GPIOD_BASE+0x08)
#define GPIOD_ODR			*(unsigned int*)(GPIOD_BASE+0x0C)
#define GPIOD_BSRR	        *(unsigned int*)(GPIOD_BASE+0x10)
#define GPIOD_BRR			*(unsigned int*)(GPIOD_BASE+0x14)
#define GPIOD_LCKR		    *(unsigned int*)(GPIOD_BASE+0x18)

2、寄存器编程

在这里插入图片描述

这个寄存器功能控制输出的数据为0或者1 。
所以我们控制LED延时闪烁就是控制ODR寄存器先输出1,LED灯亮,延时一段时间,控制ODR寄存器先输出0,LED灯灭,一直循环,实现流水灯的效果。
控制GPIOB_Pin_1输出为1

GPIOB_ODR |= (1<<1);  //1左移1位,变为0x10,与GPIOB_ODR进行或运算,将其第二位变为1

控制GPIOB_Pin_1输出为0

GPIOB_ODR &= ~(1<<1); 1左移1位,取反,与GPIOB_ODR进行与运算,将其第二位变为0

实现延时:

void delay(unsigned int i);

int main(void)
{
	// 开启GPIOA、GPIOB、GPIOC 端口时钟
	RCC_APB2ENR |= (7<<2);

	//清空控制PA12的端口位
	GPIOA_CRH &= ~( 0x0F<< (4*4));	
	// 配置PA12为通用推挽输出,速度为2M
	GPIOA_CRH |= (2<<4*4);
	
	
	//清空控制PB1的端口位
	GPIOB_CRL &= ~( 0x0F<< (4*1));	
	// 配置PB1为通用推挽输出,速度为2M
	GPIOB_CRL |= (2<<4*1);	
	
	
	//清空控制PC14的端口位
	GPIOC_CRH &= ~( 0x0F<< (4*6));	
	// 配置PC14为通用推挽输出,速度为2M
	GPIOC_CRH |= (2<<4*6);

	while(1)
	{
		GPIOB_ODR |= (1<<1);
		delay(100);                 //红灯闪烁
	  GPIOB_ODR &= ~(1<<1);
	  delay(100);
		
		GPIOA_ODR |= (1<<12);
		delay(100);					//黄灯闪烁
	  GPIOA_ODR &= ~(1<<12);
	  delay(100);
		
		GPIOC_ODR |= (1<<14);
		delay(100);					//绿灯闪烁
	  GPIOC_ODR &= ~(1<<14);
	   delay(100);		
	}

}


void delay(unsigned int i)
{
	unsigned char j;               //简单延时函数
	unsigned char k;
	for(;i>0;i--)
		for(j =500; j>0; j--) 
	       for(k =200; k>0; k--);
}

三、汇编语言实现流水灯

构建一个纯汇编的工程文件,配置工程文件环境时,不要选择’’startup’‘和’’core’

RCC_APB2ENR EQU 0x40021018

GPIOA_CRH EQU   0x40010804
GPIOA_ODR EQU   0x4001080C
                                    
GPIOB_CRL EQU   0x40010C00    ;寄存器映射
GPIOB_ODR EQU   0x40010C0C	
	
GPIOC_CRH EQU   0x40011004
GPIOC_ODR EQU   0x4001100C	
	
	
Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

                AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
		
                
MainLoop		BL LED2_Init
                BL LED2_ON
                BL Delay             ;LED2灯闪烁
                BL LED2_OFF
                BL Delay
				
				BL LED1_Init				
				BL LED1_ON
                BL Delay             ;LED1灯闪烁
                BL LED1_OFF
                BL Delay
				
                BL LED3_Init				
				BL LED3_ON
                BL Delay            ;LED3灯闪烁
                BL LED3_OFF
                BL Delay
				
                B MainLoop
				
             
LED1_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x08         ;开启端口GPIOB的时钟		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1配置为通用推挽输出
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002   
                LDR R1,=GPIOB_ODR          ;GPIO_Pin_1输出为0
                STR R0,[R1]
             
                POP {R0,R1,PC}


             
LED1_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002    ;GPIO_Pin_1输出为0,LED1熄灭
			    LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED1_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOB_ODR
                ORR R0,R0,#0X00000002    ;GPIO_Pin_1输出为1,LED1亮
                 LDR R1,=GPIOB_ODR
                STR R0,[R1]
                POP {R0,R1,PC}           


				

LED2_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04		   ;打开GPIOA的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOA_CRH
                ORR R0,R0,#0X00020000   ;GPIOA_Pin_12配置为通用推挽输出
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOA_ODR
                BIC R0,R0,#0X00001000   
                LDR R1,=GPIOA_ODR            ;GPIOA_Pin_12输出为0
                STR R0,[R1]
             
                POP {R0,R1,PC}
				
LED2_OFF
                PUSH {R0,R1, LR}   
                
               LDR R0,=GPIOA_ODR
               BIC R0,R0,#0X00001000        ;GPIOA_Pin_12输出为0,LED2熄灭
			    LDR R1,=GPIOA_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED2_ON
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOA_ODR
                ORR R0,R0,#0X00001000     ;GPIOA_Pin_12输出为1,LED2亮
				 LDR R1,=GPIOA_ODR
                STR R0,[R1]
				
				 POP {R0,R1,PC}
				 

LED3_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x10		    ;打开GPIOC的时钟
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOC_CRH
                ORR R0,R0,#0X02000000   ;GPIOC_Pin_14配置为通用推挽输出
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000   ;GPIOC_Pin_14输出为0
                LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000  ;GPIOC_Pin_14输出为0,LED3熄灭
			    LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOC_ODR
                ORR R0,R0,#0X00004000   ;GPIOC_Pin_14输出为1,LED3亮
                 LDR R1,=GPIOC_ODR
                STR R0,[R1]
				
                POP {R0,R1,PC}        
                
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1

                CMP R0,#300
                BCC DelayLoop0

  MOVS R0,#0
                   ADDS R1,R1,#1
                   CMP R1,#300
                   BCC DelayLoop0
       
                   MOVS R0,#0
                   MOVS R1,#0
                   ADDS R2,R2,#1
                   CMP R2,#15
                   BCC DelayLoop0
        POP {R0,R1,PC}    
       
                       END

stm32启动文件

	
Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

                AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 

LED端口初始化

             
LED1_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x08       ;开启端口GPIOB的时钟		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1配置为通用推挽输出,输出速度为2M
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002   ;配置GPIOB_Pin_1输出为低电平,LED灯灭
                LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}


延时

Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1

                CMP R0,#300
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#300
                BCC DelayLoop0

                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                POP {R0,R1,PC}    

实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。

四、实际演示

(一)仪器选择
1、stm32核心板103f一块
2、usb转串口一块
3、面包板一块,导线若干
(二)操作连线
GND-G
3V3-3.3
RXD-A10
TXD-A9
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、总结

经过此次实验,加深了C语言,汇编语言的认识,对GPIOx端口的各寄存器地址的了解,我也意识到了自己操作能力的不足,仍需查漏补缺。
关于面包板
参考:
《STM32F10X参考手册》
STM32寄存器的简介、地址查找,与直接操作寄存器
STM32串口下载程序
STM32最小核心板F103串口通信USART

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F10x电灯程序是一个基于STM32F10x系列微控制器的程序,用于控制一个LED灯的开关。下面是一个示例的电灯程序代码: ```c #include "stm32f10x.h" int main(void) { // 确保相关外设时钟使能 RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // 配置PC13引脚为推挽输出模式 GPIOC->CRH &= ~GPIO_CRH_MODE13; GPIOC->CRH |= GPIO_CRH_MODE13_0; GPIOC->CRH &= ~GPIO_CRH_CNF13; while (1) { // 点亮LED灯 GPIOC->BSRR = GPIO_BSRR_BS13; // 延时一段时间 for (int i = 0; i < 1000000; i++); // 关闭LED灯 GPIOC->BSRR = GPIO_BSRR_BR13; // 延时一段时间 for (int i = 0; i < 1000000; i++); } } ``` 这个程序主要包含以下几个部分: 1. 在main函数之前,系统会执行一系列的初始化操作,其中包括对微控制器的启动、嵌入式闪存接口和锁相环进行初始化,以及更新系统内核的时钟变量。 2. 程序通过配置相关的引脚和寄存器来控制LED灯的开关。在这个例子中,程序使用PC13引脚来控制LED灯,配置该引脚为推挽输出模式,即可以通过设置和复位相应的寄存器位来控制引脚的高低电平。 3. 程序进入一个无限循环,其中包含一个延时循环。在每次循环中,程序先将LED灯点亮,然后延时一段时间,再将LED灯关闭,再延时一段时间,如此循环。这样就实现了LED灯的周期性闪烁效果。 请注意,以上提供的是一个简单的示例程序,实际的电灯程序可能会根据具体需求和硬件配置有所差异。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值