stm32花式点流水灯


前言

本文是简要介绍一下不同方式实现流水灯,比较不同方式下的异同。
以STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED 搭建电路,使用GPIOA、GPIOB、GPIOC这3个端口控制LED灯,轮流闪烁,间隔时长1秒。
在这里我们采用GPIOA_Pin_12、GPIOB_Pin_1、GPIOC_Pin_14分别控制红、绿、蓝LED灯。


一、固件库流水灯

(一)新建工程

因为要使用ST固件库,这部分配置较为繁琐,具体可参考网上一些配置的流程

stm32f103c8t6工程模板的建立

我配置的工程文件模板以及参考手册也放在后面,可供参考

(二)配置GPIO端口

GPIO端口的初始化设置三步骤

  • 时钟配置
  • 输入输出模式设置
  • 最大速率设置

下面让我们跟着这个步骤开始配置GPIOB_Pin_1端口
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);

至此一个GPIOB_Pin_1配置完毕。

由于这部分代码重复性较高,可自己编写函数封装上诉过程实现代码简洁性。

所以关于流水灯的有关函数我放在led.c中并在led.h中声明 。

  • 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端口配置完成之后,我们就能控制LED点亮点灭了,但是如果我们需要实现精确延时1s后LED灯闪烁,我们就需要添加延时函数,这部分系统中断,在这里我调用的是正点原子写好的延时函数,就不过多介绍了。
在这里也可以采用软件延时(循环操作时不进行任何操作),缺点是不能实现精确延时、且运行效率低。

  • 搭建电路
    本次我们在面包板上使用c8t6控制红、绿、黄三个灯轮流闪烁,所搭建的电路图如下。
    请添加图片描述

关于面包板的基本简介,可参考

面包板使用简介

  • 接线

USB转TTL模块GND、3v3与最小系统板的地和3v3相连
USB转TTL模块的TXD接最小系统板的PA10
USB转TTL模块的RXD接最小系统板的PA9

引出最小系统板的PA12、PB1、PC14分别接红、黄、绿LED灯的正极,LED灯的负极接地。

  • 下载hex文件
    这里有几点需要注意
    关于BOOT0、BOOT1引脚的不同组合方式有多种下载方式
    请添加图片描述

关于使用串口下载,可参考

STM32串口下载程序

在下载完成时,保持最小系统板不断电的情况下,将boot0引脚从电源接到地,下载的程序就转移到存储器中,断电也可保存程序,上电之后可直接运行,不然程序被保存到内存中,断电之后就丢失了。

最终的运行效果放在最后面了。

二、寄存器流水灯

(一)寄存器映射

1.学会查找寄存器地址

以GPIOB->CRL寄存器为例,在stm32f103x中文参考手册中查找寄存器地址

  • 找到GPIOB端口的起始地址
    在这里插入图片描述
    可以看到,GPIOB端口起始地址为0X4001 0C00

  • 找到GPIO寄存器中的端口配置低寄存器(GPIOx_CRL)

在这里插入图片描述
查询得到该寄存器偏移地址为0x00,所以可以得出GPIOB->CRL的地址为(GPIOB端口的起始地址+偏移地址)0X4001 0C00+0x00=0X4001 0C00

2.写入其他寄存器地址

重复上述步骤,查找其他所需寄存器的地址

/*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)

以上包括本次实验所涉及的所有寄存器,在这里使用宏定义是为了使代码具有较好的可读性,将寄存器名宏定义为该寄存器对应的地址,不然一直对着地址操作,到了后来,自己都不知道在操作哪个寄存器了。

(二)寄存器的作用

既然要涉及到寄存器编程,那么我们了解要用到的相关寄存器的作用就挺重要的了。

以配置GPIOB_Pin_1为通用推挽输出模式为例

  • APB2 外设时钟使能寄存器(RCC_APB2ENR)

在这里插入图片描述

由于 STM32的外设很多,为了降低功耗,每个外设都对应着一个时钟,在芯片刚上电的时候这些时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。

该寄存器的作用是控制所有挂载到APB2总线上外设时钟的开关,每使用一个外设,我们都要打开对应的时钟。这里我们可以看到RCC_APB2ENR寄存器的位3控制GPIOB的时钟打开与关闭。

  • 打开GPIOB外设的时钟
 RCC_APB2ENR |= (1<<3); //将1左移3位变为0x10,与RCC_APB2ENR进行或运算 
  • 端口配置低寄存器(GPIOx_CRL)

在这里插入图片描述

GPIOx_CRL 中包含 0-7 号引脚(控制GPIO低8位),每个引脚占用 4 个寄存器 位。 MODE 位用来配置输出的速度, CNF 位用来配置各种输入输出模式。

我们配置GPIOB_Pin_1为通用推挽输出,输出速度为2M
在这里插入图片描述

    GPIOB_CRL &= ~( 0x0F<< (4*1));// 0x0F左移4位,取反,再与GPIOB_CRL进行与运算,将4-7位的数据变为0
	GPIOB_CRL |= (2<<4*0);   //2左移0位,及0x02,将第二位置为1,配置PB1为通用推挽输出,速度为2M
  • 端口输出数据寄存器(GPIOx_ODR)
    在这里插入图片描述

这个寄存器功能很简单,控制输出的数据为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--);
}

注意:在这个代码中用到4*0的作用是因为是用的是作用于GPIO_CRL(GPIO_CRL每4位控制1个引脚),将4*0中的0改为1后就方便对后续引脚进行操作,这种写法是为了后续操作的快捷。

在自己编写代码时,不要忽略上面所定义的寄存器地址。


三、汇编语言流水灯

  • 构建一个纯汇编的工程文件,配置工程文件环境时,不要选择’’startup’‘和’’core’’(因为参考程序里有自带的类似启动文件startup之类的功能,选择之后会导致冲突)

  • 代码如下

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          ;GPIOB_Pin_1输出为0
                STR R0,[R1]
             
                POP {R0,R1,PC}


             
LED1_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002    ;GPIOB_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    ;GPIOB_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 

刚刚我们说,在新建工程的时候不要选择’’startup’‘启动文件,这是因为我们在这里已经自己定义了关于startup`’'启动文件的一些功能,如果再包含startup启动文件,会引起冲突。

关于’’startup’'启动文件的简要介绍

启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:

  1. 初始化堆栈指针 SP=_initial_sp
  2. 初始化 PC 指针 =Reset_Handler
  3. 初始化中断向量表
  4. 配置系统时钟
  5. 调用 C 库函数 _main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界
  • 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}    

这部分采用的是软件延时,具体原理为R0R1R2初始化为0,R0加1,当R0大于300时,R1加1,然后R0为变为0,R0继续加1,循环往复,当R1大于300时,R2加1,R0、R1变为0,继续此操作。
这个延时函数大概持续300*300*15=1350000个指令周期。

  • 小结

本次实例汇编代码有些部分重复,导致代码有些冗余。此外,其实我使用的这种点灯方式本质上就是操作寄存器,只不过使用的是汇编语言。大家可以参考上面寄存器点灯程序比较一下两者的区别。
为了对比方便,在汇编程序中的变量命名我尽量保持与寄存器中的命名一样。

四、实际效果

上面三种方式都是控制GPIOA_Pin_12 、GPIOB_Pin_1、GPIOC_Pin_14端口的,最终实现效果均如下所示。
请添加图片描述

总结

本次实验从c语言固件库到寄存器再到汇编语言,关于实验所涉及到的也越来越底层,关于stm32寄存器的一些功能有了更深刻的理解。
在寄存器操作时,对左移、右移、异或、按位与、按位或,还有关于c语言的结构体,指针、宏定义都用上了,也算是学以致用吧。
实验不足的地方是,在使用汇编语言时没有使用精确延时,而是退而求其次,采用准确度不高的软件延时,究其原因还是不熟悉。

有关上述有不当之处,望大家不吝指教。

参考
STM32从地址到寄存器
STM32寄存器的简介、地址查找,与直接操作寄存器

参考书籍
《STM32库开发指南-基于野火指南者开发板》
《STM32F10X参考手册》

资料链接

链接:https://pan.baidu.com/s/1w5iVrx-Ob_C5z5MljxZkww
提取码:i4wl

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值