基于指南者STM32_入门

ISP- In-System Programming

ISP(In-System Programming)在系统可编程,指电路板上的空白器件可以编程写入最终用户代码,
而不需要从电路板上取下器件,已经编程的器件也可以用 ISP 方式擦除或再编程。

ISP 的时候需要用到 (bootloader) 自举程序,自举程序存储在 STM32 器件的内部自举 ROM 存储
器(系统存储器)中。其主要任务是通过一种可用的串行外设(USART、CAN、USB、I2C 等)将
应用程序下载到内部 Flash 中。每种串行接口都定义了相应的通信协议,其中包含兼容的命令集
和序列。

最常用的ISP方式就是通过串口下载,最主要的优点就是成本低,缺点是只能用于下载程序,不能硬件仿真. 普通ISP和一键ISP,普通ISP在下载程序的时候需要手动配置BOOT的启动方式,而一键ISP则通过独特的硬件电路和上位机配合使用来达到一键下载的功能。


通常的 ISP 的步骤如下:

  1. 电脑通过 USB 转串口线连接 STM32 的 USART1,并打开电脑端的上位机;
  2. 设置跳线保持 BOOT0 为高电平,BOOT1 为低电平;
  3. 复位单片机使其进入 bootloader 模式,通过上位机下载程序;
  4. 下载完毕,设置跳线保持 BOOT0 为低电平,BOOT1 为低电平;
  5. 复位单片机即可启动用户代码,正常运行。
    以上步骤有个不好的地方就是下载程序需要跳线及复位操作,很繁琐。通过对 ISP 的原理认识,
    一键 ISP 就诞生了,它需要做的事情就是用上位机去控制 BOOT0 脚和单片机的复位脚,原理图
    如下:
    在这里插入图片描述
    在这里插入图片描述

BOOT 配置

在 ISP 下载电路中,需要配置 BOOT 引脚,有关 BOOT 引脚不同的配置会产生不同的启动
方式,见下表。

BOOT0BOOT1启动方式
0X内部 FLASH
10系统存储器 (ISP)
11内部 SRAM

ISP 一键下载

一般都是用到 RXD 和 TXD 这两个口,一键 ISP 电路中我们需要用 USB 转串口的芯片的 DTR 口和 RTS 口来控制单片机的 BOOT0 和 NRST,原理如下:

  1. 通过上位机控制 U6(CH340G) 的 RTS 脚为低电平,Q1 导通,BOOT0 的电平上拉为高电平。

  2. 通过上位机控制 U6(CH340G) 的 DTR 脚为高电平,由于 RTS 为低电平,Q2 导通,U8 的 2
    脚为低电平,U18 为一个模拟开关,使能端由 4 脚控制,默认高电平,U18 的 1 脚和 2 脚
    导通,所以 NRST 为低电平系统复位。

  3. 单片机进入 ISP 模式,此时可以将 DTR 脚设置为低电平,RTS 设置为高电平。Q1 和 Q2 为
    截至状态,BOOT0 和 NRST 还原默认电平。

  4. 上位机将程序下载到单片机,下载完毕之后,程序自动运行。

  5. 至此,很多人还会认为 U18、Q1、Q2 是多余的,用 U6 的 RTS 和 DTR 直接控制也可以。正
    常情况下,这样理解没有问题,但是我们忽略了一点,就是单片机上电瞬间如果 USB 转串
    口连接了电脑,DTR 和 RTS 的电平是变化的,如果不处理好,单片机会一直进入 ISP 模式,
    或者系统会复位多次,这种情况是不允许的。

  6. 于是,就有了我们全新的一键 ISP 电路。我们主要是分析上电瞬间的逻辑关系,单片机上
    电时我们通过示波器观察波形得知 DTR 和 RTS 的电平是变化的,但是也有一个规律就是:
    只要 RTS 为低电平的时候,DTR 的电平也是低,因此一般情况 Q2 不会导通,但由于这两
    个 IO 口的电平存在“竞争冒险”,会出现 RTS 的下降沿的时候刚好遇到 DTR 的上升沿,这
    个时候 Q2 导通,导致系统复位,而 BOOT0 此时有可能也为高电平,就会进入 ISP 模式。
    这个是不受我们控制的,我们不想系统出现这样的情况。因此加入了模拟开关来切断这种
    干扰。

  7. 加入模拟开关 U18,通过控制 U18 的 4 脚的开关来达到隔离干扰电平的目的。下面我们分
    析一下延时开关电路,上电瞬间,电容 C65 通过电阻 R18 来充电,由于电阻 100k 很大,电
    容的充电电流很小,等电容充电达到 U18 的 4 脚的有效电平 2V 时,大概耗时 1S,在这个
    1S 时间内 U18 的模拟开关是断开的,因此 RTS 和 DTR 的干扰电平不会影响到系统复位。
    系统正常运行。

上位机设置

RTS低电平进入BootLoader,DTR高电平进入复位

232标准(mcuisp软件就是按照这个标准设计的

​ 逻辑1:-3~-15V, 逻辑0:+3~+15V

TTL标准

​ 逻辑1:3.3V, 逻辑0:0V

在这里插入图片描述

初始STM32

STM32,从字面上来理解,ST 是意法半导体,M 是 Microelectronics 的缩写,32 表示 32 位,合起
来理解,STM32 就是指 ST 公司开发的 32 位微控制器。

stm32能做什么

1、串口—USART,用于跟跟串口接口的设备通信,比如:USB转串口模块、ESP8266
WIFI、GPS模块,GSM 模块,串口屏、指纹识别模块

2、内部集成电路—I2C,用于跟I2C接口的设备通信,比如:EEPROM、电容屏、陀螺仪MPU6050、0.96寸OLED模块

3、串行通信接口—SPI,用于跟SPI接口的设备通信,比如:串行FLASH、以太网W5500、音频模块VS1053

4、SDIO、FSMC的超级、I2S、ADC、GPIO

在这里插入图片描述

在这里插入图片描述

分配原理图引脚

在这里插入图片描述

如何寻找引脚的功能说明

官方资料:STM32Fxxx数据手册,也叫datasheet。注意数据手册跟参考手册的区别

在这里插入图片描述
在这里插入图片描述

引脚的功能定义解读

在这里插入图片描述

寄存器

在这里插入图片描述

STM32芯片架构图

在这里插入图片描述

在这里插入图片描述

ARM设计内核(驱动单元),ST设计外设(被动单元)

ICode总线

ICode 中的 I 表示 Instruction,即指令。我们写好的程序编译之后都是一条条指令,存放在 FLASH
中,内核要读取这些指令来执行程序就必须通过 ICode 总线,它几乎每时每刻都需要被使用,它
是专门用来取指的。

驱动单元

DCode总线

DCode 中的 D 表示 Data,即数据,那说明这条总线是用来取数的。我们在写程序的时候,数据有
常量和变量两种,常量就是固定不变的,用 C 语言中的 const 关键字修饰,是放到内部的 FLASH
当中的,变量是可变的,不管是全局变量还是局部变量都放在内部的 SRAM。因为数据可以被
Dcode 总线和 DMA 总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来
仲裁,决定哪个总线在取数。

系统总线

系统总线主要是访问外设的寄存器,我们通常说的寄存器编程,即读写寄存器都是通过这根系统
总线来完成的。

DMA总线

DMA 总线也主要是用来传输数据,这个数据可以是在某个外设的数据寄存器,可以在 SRAM,
可以在内部的 FLASH。因为数据可以被 Dcode 总线和 DMA 总线访问,所以为了避免访问冲突,
在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。

被动单元

内部的闪存存储器

内部的闪存存储器即 FLASH,我们编写好的程序就放在这个地方。内核通过 ICode 总线来取里
面的指令。

内部的SPARM

内部的 SRAM,即我们通常说的 RAM,程序的变量,堆栈等的开销都是基于内部的 SRAM。内
核通过 DCode 总线来访问它。

FSMC

FSMC 的英文全称是 Flexible static memory controller,叫灵活的静态的存储器控制器,是
STM32F10xx 中一个很有特色的外设,通过 FSMC,我们可以扩展内存,如外部的 SRAM,NANDFLASH 和 NORFLASH。但有一点我们要注意的是,FSMC 只能扩展静态的内存,即名称里面的
S:static,不能是动态的内存,比如 SDRAM 就不能扩展。

AHB到APB的桥

从 AHB 总线延伸出来的两条 APB2 和 APB1 总线,上面挂载着 STM32 各种各样的特色外设。我
们经常说的 GPIO、串口、I2C、SPI 这些外设就挂载在这两条总线上,这个是我们学习 STM32 的
重点,就是要学会编程这些外设去驱动外部的各种设备。

什么叫存储器物映射

存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射。

在这里插入图片描述


通过绝对地址访问内存单元

让GPIOB端口的16个引脚输出高电平,要怎么实现?

// GPIOB 端口全部输出 高电平
*(unsigned int*)(0x40010C0C) = 0xFFFF;

什么是寄存器?

给有特定功能的内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。

什么叫存储器映射?

给存储器分配地址的过程叫存储器映射,再分配一个地址叫重映射。

STM32寄存器映射

总线基地址

在这里插入图片描述

GPIOB端口的寄存器列表

在这里插入图片描述

GPIOx端口数据输出寄存器ODR描述

在这里插入图片描述

C语言对寄存器的封装

总线和外设基址宏定义

在这里插入图片描述

让PB0输出低/高电平,要怎么实现?

#define PERIPH_BASE      ((unsigned int)0x40000000)
#define APB2PERIPH_BASE  (PERIPH_BASE + 0x00010000)
#define GPIOB_BASE       (APB2PERIPH_BASE + 0x0C00)
#define GPIOB_ODR        *(unsignedint*)(GPIOB_BASE+0x0C)

// PB0输出输出低电平
GPIOB_ODR &= ~(1<<0);

// PB0输出输出高电平
GPIOB_ODR |= (1<<0);

使用结构体封装寄存器列表

在这里插入图片描述

使用结构体指针访问寄存器

在这里插入图片描述

定义GPIO端口基地址指针

在这里插入图片描述

新建工程-寄存器版本

  1. 创建文件夹

  2. keil新建project,目录在刚创建文件夹下

  3. 当前板子为STM32F103VE

  4. 添加startup文件, startup_stm32f10x_hd.s

  5. 该文件夹创建stm32f10x.h, 用于存放寄存器映射的代码,暂时为空

  6. 该文件夹创建mian.c

    #include "stm32f10x.h"
    
    int main (void)
    {
    }
    
    void SystemInit(void)
    {
    	//函数体空,因为不引用外部文件,避免报错
    }
    
  7. 编译,产生hex,axf等用于烧录的输出文件

寄存器点灯

#include "stm32f10x.h"

int main (void)
{
	
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	//*(unsigned int *)0x40010C00 &= ~(0x111<<1);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位

}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

GPIO功能框图

GPIO

general purpose input output, 是通用输入输出端口的简称,简单来说就是软件可控制的引脚,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能

关于不同引脚的不同功能得查看datasheet

在这里插入图片描述


在这里插入图片描述

IO口直接驱动电机时,电机启动时,反向电流也大,会产生反向电动势,会有过冲,积分时间很短,保护二极管还未起到作用,过充电压直接进入芯片内部,损坏芯片.需要驱动电路

推挽输出

推挽输出

在这里插入图片描述

INT -> ODR -> 0/1

总结:什么叫推挽输出?

1、可以输出高低电平,用于连接数字器件,高电平由VDD决定,低电平由VSS决定。

2、推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候另外一个截止,优点开关效率效率高,电流大,驱动能力强。

3、输出高电平时,电流输出到负载,叫灌电流,可以理解成推,输出低电平时,负载电流流向芯片,叫拉电流,即挽。

开漏输出

开漏输出

在这里插入图片描述

需要上拉电阻来输出高电平 (3.3v/5v)

总结:什么叫开漏输出?

1、只能输出低电平,不能输出高电平。

2、如果要输出高电平,则需要外接上拉。

3、开漏输出具有“线与”功能,一个为低,全部为低,多用于I2C和SMBUS总线。

GPIO输出初始化顺序

1、选定具体的GPIO

2、配置GPIO工作模式(CRL和CRH寄存器)

3、控制GPIO输出高低电平(ODR、BRR和BSRR)

构建库函数雏形

寄存器点灯_类似固件库方法

//stm32f10x.h
//用来存放STM32寄存器映射的代码

//外设  peripheral

#define PERIPHERAL_BASE		((unsigned int)0x40000000)
#define APB1PERIPHERAL		PERIPHERAL_BASE
#define APB2PERIPHERAL		(PERIPHERAL_BASE+0x10000)
#define AHBPERIPHERAL		(PERIPHERAL_BASE+0x20000)

#define RCC_BASE					(AHBPERIPHERAL+0x1000)
#define GPIOB_BASE				(APB2PERIPHERAL+0x0c00)

#define RCC_APB2ENR				*(unsigned int *)(RCC_BASE+0x18)

#define GPIOB_CRL					*(unsigned int *)(GPIOB_BASE+0x00)
#define GPIOB_CRH					*(unsigned int *)(GPIOB_BASE+0x04)
#define GPIOB_ODR					*(unsigned int *)(GPIOB_BASE+0x0c)
	
#include "stm32f10x.h"

int main (void)
{
#if 0
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	*(unsigned int *)0x40010C00 &= ~(0xf);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位
#else
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	//配置IO口为输出 0x0001
	GPIOB_CRL &= ~(0xf);
	GPIOB_CRL |= (0x1<<0);
	//控制 ODR 寄存器
	GPIOB_ODR  &= ~(1<<0);
	
#endif
}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

寄存器点灯_C语言结构体

在这里插入图片描述

//用来存放STM32寄存器映射的代码

//外设  peripheral

#define PERIPHERAL_BASE		((unsigned int)0x40000000)
#define APB1PERIPHERAL		PERIPHERAL_BASE
#define APB2PERIPHERAL		(PERIPHERAL_BASE+0x10000)
#define AHBPERIPHERAL		(PERIPHERAL_BASE+0x20000)

#define RCC_BASE					(AHBPERIPHERAL+0x1000)
#define GPIOB_BASE				(APB2PERIPHERAL+0x0c00)

#define RCC_APB2ENR				*(unsigned int *)(RCC_BASE+0x18)

#define GPIOB_CRL					*(unsigned int *)(GPIOB_BASE+0x00)
#define GPIOB_CRH					*(unsigned int *)(GPIOB_BASE+0x04)
#define GPIOB_ODR					*(unsigned int *)(GPIOB_BASE+0x0c)

typedef unsigned int		uint32_t;
typedef unsigned short		uint16_t;
typedef struct
{
	uint32_t CRL;
	uint32_t CRH;
	uint32_t IDR;
	uint32_t ODR;
	uint32_t BSRR;
	uint32_t BRR;
	uint32_t LCKR;
}GPIO_TypeDef;

#define GPIOB		((GPIO_TypeDef*)GPIOB_BASE)

#include "stm32f10x.h"

int main (void)
{
#if 0
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	*(unsigned int *)0x40010C00 &= ~(0xf);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位
#elif 0
	
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB_CRL &= ~(0xf);
	GPIOB_CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB_ODR  &= ~(1<<0);
	
#elif 1
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB->ODR  &= ~(1<<0);
	
#endif
}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

寄存器点灯_GPIO置位/清除位函数

新建stm32f10x_gpio.c,stm32f10x_gpio.h文件;project加入C文件, C文件include H文件.

#ifndef _STM32F10X_GPIO_H
#define _STM32F10X_GPIO_H

#include "stm32f10x.h"

#define GPIO_Pin_0    ((uint16_t)0x0001)  /*!< 选择Pin0 */    //(00000000 00000001)b
#define GPIO_Pin_1    ((uint16_t)0x0002)  /*!< 选择Pin1 */    //(00000000 00000010)b
#define GPIO_Pin_2    ((uint16_t)0x0004)  /*!< 选择Pin2 */    //(00000000 00000100)b
#define GPIO_Pin_3    ((uint16_t)0x0008)  /*!< 选择Pin3 */    //(00000000 00001000)b
#define GPIO_Pin_4    ((uint16_t)0x0010)  /*!< 选择Pin4 */    //(00000000 00010000)b
#define GPIO_Pin_5    ((uint16_t)0x0020)  /*!< 选择Pin5 */    //(00000000 00100000)b
#define GPIO_Pin_6    ((uint16_t)0x0040)  /*!< 选择Pin6 */    //(00000000 01000000)b
#define GPIO_Pin_7    ((uint16_t)0x0080)  /*!< 选择Pin7 */    //(00000000 10000000)b

#define GPIO_Pin_8    ((uint16_t)0x0100)  /*!< 选择Pin8 */    //(00000001 00000000)b
#define GPIO_Pin_9    ((uint16_t)0x0200)  /*!< 选择Pin9 */    //(00000010 00000000)b
#define GPIO_Pin_10   ((uint16_t)0x0400)  /*!< 选择Pin10 */   //(00000100 00000000)b
#define GPIO_Pin_11   ((uint16_t)0x0800)  /*!< 选择Pin11 */   //(00001000 00000000)b
#define GPIO_Pin_12   ((uint16_t)0x1000)  /*!< 选择Pin12 */   //(00010000 00000000)b
#define GPIO_Pin_13   ((uint16_t)0x2000)  /*!< 选择Pin13 */   //(00100000 00000000)b
#define GPIO_Pin_14   ((uint16_t)0x4000)  /*!< 选择Pin14 */   //(01000000 00000000)b
#define GPIO_Pin_15   ((uint16_t)0x8000)  /*!< 选择Pin15 */   //(10000000 00000000)b
#define GPIO_Pin_All  ((uint16_t)0xFFFF)  /*!< 选择全部引脚*/ //(11111111 11111111)b

void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

#endif /*_STM32F10X_GPIO_H*/
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

int main (void)
{
#if 0
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	*(unsigned int *)0x40010C00 &= ~(0xf);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位
#elif 0
	
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB_CRL &= ~(0xf);
	GPIOB_CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB_ODR  &= ~(1<<0);
	
#elif 0
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB->ODR  &= ~(1<<0);
#elif 1
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	//GPIO_SetBits(GPIOB, GPIO_Pin_0);
	GPIO_ResetBits(GPIOB, GPIO_Pin_0);

	
#endif
}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

寄存器点灯_IO初始化

#ifndef _STM32F10X_GPIO_H
#define _STM32F10X_GPIO_H

#include "stm32f10x.h"

#define GPIO_Pin_0    ((uint16_t)0x0001)  /*!< 选择Pin0 */    //(00000000 00000001)b
#define GPIO_Pin_1    ((uint16_t)0x0002)  /*!< 选择Pin1 */    //(00000000 00000010)b
#define GPIO_Pin_2    ((uint16_t)0x0004)  /*!< 选择Pin2 */    //(00000000 00000100)b
#define GPIO_Pin_3    ((uint16_t)0x0008)  /*!< 选择Pin3 */    //(00000000 00001000)b
#define GPIO_Pin_4    ((uint16_t)0x0010)  /*!< 选择Pin4 */    //(00000000 00010000)b
#define GPIO_Pin_5    ((uint16_t)0x0020)  /*!< 选择Pin5 */    //(00000000 00100000)b
#define GPIO_Pin_6    ((uint16_t)0x0040)  /*!< 选择Pin6 */    //(00000000 01000000)b
#define GPIO_Pin_7    ((uint16_t)0x0080)  /*!< 选择Pin7 */    //(00000000 10000000)b

#define GPIO_Pin_8    ((uint16_t)0x0100)  /*!< 选择Pin8 */    //(00000001 00000000)b
#define GPIO_Pin_9    ((uint16_t)0x0200)  /*!< 选择Pin9 */    //(00000010 00000000)b
#define GPIO_Pin_10   ((uint16_t)0x0400)  /*!< 选择Pin10 */   //(00000100 00000000)b
#define GPIO_Pin_11   ((uint16_t)0x0800)  /*!< 选择Pin11 */   //(00001000 00000000)b
#define GPIO_Pin_12   ((uint16_t)0x1000)  /*!< 选择Pin12 */   //(00010000 00000000)b
#define GPIO_Pin_13   ((uint16_t)0x2000)  /*!< 选择Pin13 */   //(00100000 00000000)b
#define GPIO_Pin_14   ((uint16_t)0x4000)  /*!< 选择Pin14 */   //(01000000 00000000)b
#define GPIO_Pin_15   ((uint16_t)0x8000)  /*!< 选择Pin15 */   //(10000000 00000000)b
#define GPIO_Pin_All  ((uint16_t)0xFFFF)  /*!< 选择全部引脚*/ //(11111111 11111111)b

typedef enum		//枚举
{
	GPIO_Speed_10MHZ	= 1,	
	GPIO_Speed_2MHZ,			//二进制;自动往下加1
	GPIO_Speed_50MHZ,
}GPIOSpeed_TypeDef;

typedef enum
{ GPIO_Mode_AIN = 0x0,           // 模拟输入     (0000 0000)b
  GPIO_Mode_IN_FLOATING = 0x04,  // 浮空输入     (0000 0100)b
  GPIO_Mode_IPD = 0x28,          // 下拉输入     (0010 1000)b
  GPIO_Mode_IPU = 0x48,          // 上拉输入     (0100 1000)b
  
  GPIO_Mode_Out_OD = 0x14,       // 开漏输出     (0001 0100)b
  GPIO_Mode_Out_PP = 0x10,       // 推挽输出     (0001 0000)b
  GPIO_Mode_AF_OD = 0x1C,        // 复用开漏输出 (0001 1100)b
  GPIO_Mode_AF_PP = 0x18         // 复用推挽输出 (0001 1000)b
}GPIOMode_TypeDef;

typedef struct
{
	uint16_t GPIO_Pin;
	uint16_t GPIO_Speed;
	uint16_t GPIO_Mode;
}GPIO_InitTypeDef;



void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

#endif /*_STM32F10X_GPIO_H*/

#include "stm32f10x_gpio.h"

void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	GPIOx->BSRR |=	GPIO_Pin;
}

void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	GPIOx->BRR |=	GPIO_Pin;
}

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  
/*---------------------- GPIO 模式配置 --------------------------*/
  // 把输入参数GPIO_Mode的低四位暂存在currentmode
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
	
  // bit4是1表示输出,bit4是0则是输入 
  // 判断bit4是1还是0,即首选判断是输入还是输出模式
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
	// 输出模式则要设置输出速度
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  // 配置端口低8位,即Pin0~Pin7
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
	// 先备份CRL寄存器的值
    tmpreg = GPIOx->CRL;
		
	// 循环,从Pin0开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
	 // pos的值为1左移pinpos位
      pos = ((uint32_t)0x01) << pinpos;
      
	  // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			
	  //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		// pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
       //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);  
				
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }				
        else
        {
          // 判断是否为上拉输入模式
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
		    // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
		// 把前面处理后的暂存值写入到CRL寄存器之中
    GPIOx->CRL = tmpreg;
  }
/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  // 配置端口高8位,即Pin8~Pin15
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
		// // 先备份CRH寄存器的值
    tmpreg = GPIOx->CRH;
		
	// 循环,从Pin8开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
			
      // pos与输入参数GPIO_PIN作位与运算
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
			
	 //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		//pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
        
	    //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
         // 判断是否为上拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
		  // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
	// 把前面处理后的暂存值写入到CRH寄存器之中
    GPIOx->CRH = tmpreg;
  }
}

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

int main (void)
{
#if 0
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	*(unsigned int *)0x40010C00 &= ~(0xf);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位
#elif 0
	
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB_CRL &= ~(0xf);
	GPIOB_CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB_ODR  &= ~(1<<0);
	
#elif 0
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB->ODR  &= ~(1<<0);
#elif 0
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	//GPIO_SetBits(GPIOB, GPIO_Pin_0);
	GPIO_ResetBits(GPIOB, GPIO_Pin_0);

#elif 1
	GPIO_InitTypeDef GPIO_InitStructure;
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);

	GPIO_InitStructure.GPIO_Pin = 			GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHZ;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_SetBits(GPIOB, GPIO_Pin_1);
	//GPIO_ResetBits(GPIOB, GPIO_Pin_1);
	GPIO_ResetBits(GPIOB, GPIO_Pin_5);
	
#endif
}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

SUM_UP

#ifndef _STM32F10X_GPIO_H
#define _STM32F10X_GPIO_H

#include "stm32f10x.h"

#define GPIO_Pin_0    ((uint16_t)0x0001)  /*!< 选择Pin0 */    //(00000000 00000001)b
#define GPIO_Pin_1    ((uint16_t)0x0002)  /*!< 选择Pin1 */    //(00000000 00000010)b
#define GPIO_Pin_2    ((uint16_t)0x0004)  /*!< 选择Pin2 */    //(00000000 00000100)b
#define GPIO_Pin_3    ((uint16_t)0x0008)  /*!< 选择Pin3 */    //(00000000 00001000)b
#define GPIO_Pin_4    ((uint16_t)0x0010)  /*!< 选择Pin4 */    //(00000000 00010000)b
#define GPIO_Pin_5    ((uint16_t)0x0020)  /*!< 选择Pin5 */    //(00000000 00100000)b
#define GPIO_Pin_6    ((uint16_t)0x0040)  /*!< 选择Pin6 */    //(00000000 01000000)b
#define GPIO_Pin_7    ((uint16_t)0x0080)  /*!< 选择Pin7 */    //(00000000 10000000)b

#define GPIO_Pin_8    ((uint16_t)0x0100)  /*!< 选择Pin8 */    //(00000001 00000000)b
#define GPIO_Pin_9    ((uint16_t)0x0200)  /*!< 选择Pin9 */    //(00000010 00000000)b
#define GPIO_Pin_10   ((uint16_t)0x0400)  /*!< 选择Pin10 */   //(00000100 00000000)b
#define GPIO_Pin_11   ((uint16_t)0x0800)  /*!< 选择Pin11 */   //(00001000 00000000)b
#define GPIO_Pin_12   ((uint16_t)0x1000)  /*!< 选择Pin12 */   //(00010000 00000000)b
#define GPIO_Pin_13   ((uint16_t)0x2000)  /*!< 选择Pin13 */   //(00100000 00000000)b
#define GPIO_Pin_14   ((uint16_t)0x4000)  /*!< 选择Pin14 */   //(01000000 00000000)b
#define GPIO_Pin_15   ((uint16_t)0x8000)  /*!< 选择Pin15 */   //(10000000 00000000)b
#define GPIO_Pin_All  ((uint16_t)0xFFFF)  /*!< 选择全部引脚*/ //(11111111 11111111)b

typedef enum		//枚举
{
	GPIO_Speed_10MHZ	= 1,	
	GPIO_Speed_2MHZ,			//二进制;自动往下加1
	GPIO_Speed_50MHZ,
}GPIOSpeed_TypeDef;

typedef enum
{ GPIO_Mode_AIN = 0x0,           // 模拟输入     (0000 0000)b
  GPIO_Mode_IN_FLOATING = 0x04,  // 浮空输入     (0000 0100)b
  GPIO_Mode_IPD = 0x28,          // 下拉输入     (0010 1000)b
  GPIO_Mode_IPU = 0x48,          // 上拉输入     (0100 1000)b
  
  GPIO_Mode_Out_OD = 0x14,       // 开漏输出     (0001 0100)b
  GPIO_Mode_Out_PP = 0x10,       // 推挽输出     (0001 0000)b
  GPIO_Mode_AF_OD = 0x1C,        // 复用开漏输出 (0001 1100)b
  GPIO_Mode_AF_PP = 0x18         // 复用推挽输出 (0001 1000)b
}GPIOMode_TypeDef;

typedef struct
{
	uint16_t GPIO_Pin;
	uint16_t GPIO_Speed;
	uint16_t GPIO_Mode;
}GPIO_InitTypeDef;



void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

#endif /*_STM32F10X_GPIO_H*/

#include "stm32f10x_gpio.h"

void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	GPIOx->BSRR |=	GPIO_Pin;
}

void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	GPIOx->BRR |=	GPIO_Pin;
}

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  
/*---------------------- GPIO 模式配置 --------------------------*/
  // 把输入参数GPIO_Mode的低四位暂存在currentmode
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
	
  // bit4是1表示输出,bit4是0则是输入 
  // 判断bit4是1还是0,即首选判断是输入还是输出模式
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
	// 输出模式则要设置输出速度
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  // 配置端口低8位,即Pin0~Pin7
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
	// 先备份CRL寄存器的值
    tmpreg = GPIOx->CRL;
		
	// 循环,从Pin0开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
	 // pos的值为1左移pinpos位
      pos = ((uint32_t)0x01) << pinpos;
      
	  // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			
	  //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		// pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
       //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);  
				
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }				
        else
        {
          // 判断是否为上拉输入模式
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
		    // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
		// 把前面处理后的暂存值写入到CRL寄存器之中
    GPIOx->CRL = tmpreg;
  }
/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  // 配置端口高8位,即Pin8~Pin15
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
		// // 先备份CRH寄存器的值
    tmpreg = GPIOx->CRH;
		
	// 循环,从Pin8开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
			
      // pos与输入参数GPIO_PIN作位与运算
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
			
	 //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		//pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
        
	    //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
         // 判断是否为上拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
		  // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
	// 把前面处理后的暂存值写入到CRH寄存器之中
    GPIOx->CRH = tmpreg;
  }
}

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

#define	LEDG_GPIO_PORT	GPIOB
#define	LEDG_GPIO_CLK_ENABLE	RCC->APB2ENR |= (0x1<<3)
#define	LEDG_GPIO_PIN		GPIO_Pin_1



int main (void)
{
#if 0
	//打开GPIOB端口的时钟
	*(unsigned int *)0x40021018 |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	*(unsigned int *)0x40010C00 &= ~(0xf);
	*(unsigned int *)0x40010C00 |= (0x1<<0);	//PB1 左移4位;PB5 左移20位
	
	//控制 ODR 寄存器
	*(unsigned int *)0x40010C0C &= ~(1<<0);		//PB1 左移1位;PB5 左移5位
#elif 0
	
	//打开GPIOB端口的时钟
	RCC_APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB_CRL &= ~(0xf);
	GPIOB_CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB_ODR  &= ~(1<<0);
	
#elif 0
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	GPIOB->ODR  &= ~(1<<0);
#elif 0
	//打开GPIOB端口的时钟
	RCC->APB2ENR |= (0x1<<3);
	
	//配置IO口为输出 0x0001
	GPIOB->CRL &= ~(0xf);
	GPIOB->CRL |= (0x1<<0);
	
	//控制 ODR 寄存器
	//GPIO_SetBits(GPIOB, GPIO_Pin_0);
	GPIO_ResetBits(GPIOB, GPIO_Pin_0);

#elif 1
	GPIO_InitTypeDef GPIO_InitStructure;
	//打开GPIOB端口的时钟
	//RCC->APB2ENR |= (0x1<<3);
	LEDG_GPIO_CLK_ENABLE;

	GPIO_InitStructure.GPIO_Pin = LEDG_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHZ;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	//GPIO_SetBits(GPIOB, LEDG_GPIO_PIN);
	//GPIO_ResetBits(GPIOB, GPIO_Pin_1);
	GPIO_ResetBits(GPIOB, LEDG_GPIO_PIN);
	
#endif
}

void SystemInit(void)
{
	//函数体空,因为不引用外部文件,避免报错
}

初始固件库

1, 汇编编写的启动文件

startup_stm32f10x_hd.s:设置堆栈指针, 设置PC指针,初始化中断向量表,配置系 统时钟,调用C库函数_main,最终去到C的世界

2,时钟配置文件

system_stm32f10x.c:把外部时钟HSE = 8M,经过PLL倍频为72M

3,外设相关的

stm32f10x.h:实现了内核之外的外设的寄存器映像

​ xx:GPIO, USART, I2C, SPI, FSMC

stm32f10x_xx.c:外设的驱动函数库文件

stm32f10x_xx.h:存放外设的初始化结构体,外设初始化结构体成员的参数列表,外设固件库函数的声明

4,内核相关的

​ CMSIS - Cortex 微控制器软件接口标准

core_cm3.h:实现了内核里面外设的寄存器映像

core_cm3.c:内核外设固件库

​ NVIC (嵌套向量中断控制器), SysTick (系统滴答定时器)

misc.h

misc.c

5, 头文件的配置文件

stm32f10x_conf.h: 头文件的头文件

stm32f10x_usart.h

stm32f10x_i2c.h

stm32f10x_spi.h

stm32f10x_adc.h

stm32f10x_fsmc.h

​ …

6, 专门存放中断服务函数的C文件

stm32f10x_it.c

stm32f10x_it.h

​ 中断服务函数可以放在其他地方,并不是一定要放在stm32f10x_it.c

#include "stm32f10x.h"	//相当于51单片机中的 #inclulde <reg51.h>

int main(void)
{
	//来到这里的时候,系统的时钟已经配置成72M
}

新建工程_固件库

1, 创建文件夹

  • Project
  • Libraries
  • User
  • Doc

2, 复制固件库里Libraries的CMSISSTM32F10x_StdPeriph_Driver到当前文件夹的Libraries里

​ CMSIS只保留如图(文件易于阅读和寻找):

​ startup 文件夹只保留arm里面文件,并拷贝到上一级,并把arm删除

在这里插入图片描述

​ STM32F10x_StdPeriph_Driver 不做更改

3, 复制【固件库】STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template中的3文件到USER,并添加自己的Main文件

在这里插入图片描述


接下来在keil project里面添加汇编文件和C文件:

在这里插入图片描述


最后添加IDE 定义: USE_STDPERIPH_DRIVER,STM32F10X_HD

•STM32F10X_HD 宏:为了告诉 STM32 标准库,我们使用的芯片类型是 STM32 型号是大容
量的,使 STM32 标准库根据我们选定的芯片型号来配置。
• USE_STDPERIPH_DRIVER 宏:为了让 stm32f10x.h 包含 stm32f10x_conf.h 这个头文件。

在这里插入图片描述

并添加H文件路径,KEIL 默认在安装目录添加路径(X)

在这里插入图片描述

固件库点灯

//bsp: board support package
#include "bsp_led.h"


void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
}

#ifndef _BSP_LED_H
#define _BSP_LED_H

#include "stm32f10x.h"

#define LED_G_GPIO_PIN GPIO_Pin_0


#define ON  1
#define OFF 0

// \  C语言里面叫续航符,后面不能有任何的东西
#define LED_G(a)	if(a)\
	                      GPIO_ResetBits(GPIOB, LED_G_GPIO_PIN); \
                         else GPIO_SetBits(GPIOB, LED_G_GPIO_PIN)


void LED_GPIO_Config(void);

#endif /*_BSP_LED_H*/

#include "stm32f10x.h"	//相当于51单片机中的 #inclulde <reg51.h>
#include "bsp_led.h"

int main(void)
{
	//来到这里的时候,系统的时钟已经配置成72M
	LED_GPIO_Config();
	

	LED_G(0);
	
	
}

固件库按键检测

具体看历程

位带操作

GPIOB->ODR |= 0<<0;
P0 = 0XFE;	//总线操作

sbit LED1 = P0^0	//位操作
LED1 = 0;
位带操作公式()
((GPIOB_ODR_Addr & 0xF0000000)+0x02000000+((GPIOB_ODR_Addr & 0x00FFFFFF)<<5)+(bitnum<<2))

/*
 *addr & 0xF0000000,取地址的高4位,看看是2还是4,用于区分SRAM和外设地址,
 *如果是2,+0x02000000则=0X2200 0000,即是SRAM,如果是4,+0x02000000则=0X4200 0000,即是外设
 *
 *addr & 0x000FFFFFF,屏蔽掉高两位,相当于-0X2000 0000或者-0X4000 0000,结果表示偏移位带区多少个字节
 *<<5  等于*8*4,因为位带区一个地址表示一个字节,一个字节有8个bit,一个bit可以膨胀成一个字,即4个字节
 *<<2 等于*4,因为一个位可以膨胀成一个字,即4个字节
 *
 *分解成两条公式应该就是这样:
 *SRAM位带别名地址
 *AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
 *外设位带别名地址
 *AliasAddr= 0x22000000+((A-0x20000000)*8+n)*4 =0x22000000+ (A-0x20000000)*8*4 +n*4
 */
#include "stm32f10x.h"	//相当于51单片机中的 #inclulde <reg51.h>
#include "bsp_led.h"
#include "bsp_key.h"

#define GPIOB_ODR_Addr	(GPIOB_BASE + 0x0c)
#define PBout(bitnum) *(unsigned int*)((GPIOB_ODR_Addr & 0xF0000000)+0x02000000+((GPIOB_ODR_Addr & 0x00FFFFFF)<<5)+(bitnum<<2))

#define GPIOA_IDR_Addr	(GPIOA_BASE + 0x08)
#define PAin(bitnum) *(unsigned int*)((GPIOA_IDR_Addr & 0xF0000000)+0x02000000+((GPIOA_IDR_Addr & 0x00FFFFFF)<<5)+(bitnum<<2)) 

void Delay(uint32_t count)
{
	for(;count != 0; count--);
}

int main(void)
{
	//来到这里的时候,系统的时钟已经配置成72M
	LED_GPIO_Config();
	KEY_GPIO_Config();
#if 0
	while(1)
	{
		//LED_G(OFF);
		PBout(0) = 1;
		Delay(0xfffff2);
		//LED_G(ON);
		PBout(0) = 0;
		Delay(0xfffff2);
	}
#else
	while(1)
	{
		if(PAin(0)==KEY_ON)
		{
			while(PAin(0)==KEY_ON);
			LED_G_TOGGLE;
		}
	}
#endif
		
}

启动文件详解

初始化堆栈指针(stack pointer)

初始化PC指针 == Reset handler

初始化中断向量表

配置系统时钟

调用C库函数_main. 最终去到C的世界

  • 内存,储存变量(局部/全局)和返回地址 (desn2000学过)
  • 配置堆,主要用于动态内存的分配,malloc()
  • 初始化中断向量表
  • 复位程序
  • 中断服务函数
  • 堆栈初始化,这个由C库函数_main来实现
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: lwip是一个轻量级的TCP/IP协议栈,其使用广泛且性能稳定,适用于许多嵌入式系统,包括stm32开发板等。此开发指南主要介绍如何使用lwip在stm32上实现网络应用开发,让开发者快速掌握lwip协议栈的基本原理和使用方法。 该指南主要分为三大部分。第一部分介绍lwip协议栈的基础知识,包括协议栈的体系结构、协议栈的配置和使用、协议栈的套接字编程等。第二部分介绍如何基于lwip协议栈在stm32上实现各种网络应用开发,包括Web服务器、FTP服务器、SMTP客户端等。第三部分展示了lwip协议栈在实际应用中的案例,例如基于lwip协议栈实现的MQTT协议栈等。 在该指南的学习过程中,开发者将学习如何通过lwip协议栈来实现基于TCP/IP协议的网络应用开发,了解lwip协议栈的核心原理和使用方法,并掌握如何在stm32等嵌入式系统中实现各种网络需求。 总之,lwip应用开发实战指南 基于stm32,对于想要学习lwip协议栈的开发人员来说,是一份难得的资料,不论是对于初学者还是有经验的开发人员来说都非常有价值。 ### 回答2: lwIP(lightweight IP)是一个轻量级的网络协议栈,它的目的是为嵌入式系统提供TCP/IP协议栈的支持。基于lwIP,我们可以在嵌入式系统中使用标准的TCP/IP协议栈,从而实现网络通信。而STM32是一种嵌入式系统芯片,因此在STM32平台上实现lwIP应用开发是很有必要的。 《lwIP应用开发实战指南 基于STM32》一书就是一本介绍如何在STM32平台上实现lwIP应用开发的实用指南。本书主要包括以下几个方面的内容: 1. lwIP协议栈的概念与原理:介绍lwIP协议栈的基本原理和架构,以及其在嵌入式系统中的应用。 2. STM32的网络接口配置:介绍如何在STM32平台上配置网络接口,包括MAC层和物理层的配置。 3. lwIP协议栈的移植:介绍如何将lwIP协议栈移植到STM32平台上,并对移植过程中遇到的问题进行解决。 4. TCP/IP应用程序设计:介绍如何使用lwIP协议栈实现TCP/IP应用程序,如HTTP服务器、FTP服务器、Telnet服务器等。 5. 实验与案例分析:通过实验和案例分析,展示如何在STM32平台上实现基于lwIP协议栈的网络应用开发。 通过本书的学习,读者可以深入了解lwIP协议栈的原理和应用方法,掌握在STM32平台上实现lwIP应用开发的技术,为嵌入式网络应用开发提供有力的指导和支持。 ### 回答3: lwip应用开发实战指南,基于stm32,是一本搭载lwip协议的应用开发指南。 在实际应用中,lwip协议不仅可以加速网络数据传输,而且可以最大限度地减小网络堵塞的情况。对于开发者来说,可以借助这本文档快速上手开发网络应用。 本文档不只是提供一件简单的lwip API介绍,还会从实际应用场景出发,介绍lwip的运作原理和如何搭载在stm32开发板上。通过详细的实例和代码以及巧妙的注释,读者能够学习到lwip协议的所有特点、实现以及如何将应用开发与lwip结合,从而快速实现自己的应用场景。 当读者需要开发网络应用时,这本文档会成为他们的实战指南,帮助开发者快速搭配适用于lwip协议的开发板,实现各类网络应用任务,如socket通信、HTTP服务器等等。 总之,lwip应用开发实战指南,基于stm32指南,是一本十分值得推荐的实用指南,对于刚入门的读者和深入学习lwip协议的读者都非常有价值。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值