IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 以 STM32点亮流水灯 -> 正文阅读

[嵌入式]以 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

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-24 15:04:06  更:2021-10-24 15:04:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:45:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码