一:编译第一个程序
int main() //主函数 { } void SystemInit() //在执行主函数前,会被调用。不进行实现。在启动文件中被调用 { }
; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
二:LED灯原理图
注意:其中PB0是绿灯
三:LED点亮--代码实现
(一)第一步:点亮LED灯PB0引脚绿灯,必须设置端口输出数据寄存器,使得其对应的引脚输出低电平,产生电压差。点亮绿灯
*(unsigned int*)(0x40010c0c) &= ~(1<<0);
(二)第二步: 需要设置端口低寄存器,设置低八位0-7,设置为通用推挽输出模式,最大速度设置为10MHZ
*(unsigned int*)(0x40010c00) |= (1<<0);
开漏和推挽区别在于:
开漏:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
推挽输出:可以输出高,低电平,连接数字器件。
开漏电路就是指以MOS FET的漏极为输出的电路。一般的用法是会在漏极外部的电路添加上拉电阻。完整的开漏电路应该由开漏器件和开漏上拉电阻组成。
推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止
引入一句话:“复用输出当前的引脚功能功能有外设控制,并不由通用IO控制,因此你要通过操作GPIO来驱动LED的话,肯定是用通用的”
速度可以随便选择
(三)第三步:设置外设GPIOB端口的时钟开启
*(unsigned int*)(0x40021018) |= (1<<3);
(四)全部代码
int main() { *(unsigned int*)(0x40021018) |= (1<<3); *(unsigned int*)(0x40010c00) |= (1<<0); *(unsigned int*)(0x40010c0c) &= ~(1<<0); } void SystemInit() { }
(五)实现状态
四:实现寄存器映射
(一)实现头文件
//用于存放STM32寄存器映射代码 //外设 perirhral #define PERIRH_BASE (unsigned int)0x40000000 //前面unsigned int表示的是地址是32位 #define APB1PERIRH_BASE PERIRH_BASE #define APB2PERIRH_BASE (PERIRH_BASE+0x10000) #define AHBPERIRH_BASE (PERIRH_BASE+0x20000) #define RCC_BASE (AHBPERIRH_BASE+0x1000) #define GPIOB_BASE (APB2PERIRH_BASE+0x0c00) #define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18) //下面是实际要操作的IO口,使用*(unsigned int*)进行数据写入和读取 #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0c) #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE+0x00) #define GPIOB_CRH *(unsigned int*)(GPIOB_BASE+0x04)
(二)对主函数改写
int main() { //第三步:设置外设GPIOB端口的时钟开启 RCC_APB2ENR |= (1<<3); //第二步: 需要设置端口低寄存器,设置低八位0-7,设置为通用推挽输出模式,最大速度设置为10MHZ//注意:配置模式是,需要先进行清零 GPIOB_CRL &= ~(0x0f); //清零 GPIOB_CRL |= (1<<0); //第一步:点亮LED灯PB0引脚绿灯,必须设置端口输出数据寄存器,使得其对应的引脚输出低电平,产生电压差。点亮绿灯 GPIOB_ODR &= ~(1<<0); //只需要设置低16位即可 }
五:实现库函数初步
(一)stm32F10x.h头文件,定义引脚
#ifndef _STM32F10X_H_ #define _STM32F10X_H_ #define PERIRH_BASE (unsigned int)0x40000000 #define APB1PERIRH_BASE PERIRH_BASE #define APB2PERIRH_BASE (PERIRH_BASE+0x10000) #define AHBPERIRH_BASE (PERIRH_BASE+0x20000) #define RCC_BASE (AHBPERIRH_BASE+0x1000) #define GPIOB_BASE (APB2PERIRH_BASE+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_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) 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; typedef struct { uint32_t CR; uint32_t CFGR; uint32_t CIR; uint32_t APB2RSTR; uint32_t APB1RSTR; uint32_t AHBENR; uint32_t APB2ENR; uint32_t APB1ENR; uint32_t BDCR; uint32_t CSR; }RCC_Def; #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE) #define RCC ((RCC_Def*)RCC_BASE) #endif
(二)stmF10x_gpio.h头文件用于实现GPIO类型口的相关声明和定义
#ifndef _STM32F10X_GPIO_H_ #define _STM32F10X_GPIO_H_ #include "stm32F10x.h" #define GPIO_PIN_0 ((uint16_t)0x0001) #define GPIO_PIN_1 ((uint16_t)0x0002) #define GPIO_PIN_2 ((uint16_t)0x0004) #define GPIO_PIN_3 ((uint16_t)0x0008) #define GPIO_PIN_4 ((uint16_t)0x0010) #define GPIO_PIN_5 ((uint16_t)0x0020) #define GPIO_PIN_6 ((uint16_t)0x0040) #define GPIO_PIN_7 ((uint16_t)0x0080) #define GPIO_PIN_8 ((uint16_t)0x0100) #define GPIO_PIN_9 ((uint16_t)0x0200) #define GPIO_PIN_10 ((uint16_t)0x0400) #define GPIO_PIN_11 ((uint16_t)0x0800) #define GPIO_PIN_12 ((uint16_t)0x1000) #define GPIO_PIN_13 ((uint16_t)0x2000) #define GPIO_PIN_14 ((uint16_t)0x4000) #define GPIO_PIN_15 ((uint16_t)0x8000) #define GPIO_PIN_ALL ((uint16_t)0xFFFF) typedef enum { GPIO_Speed_10MHZ = 1, GPIO_Speed_2MHZ, 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
(三)stmF10x_gpio.c实现头文件中的函数
#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 LED_G_GPIO_CLK_ENABLE RCC->APB2ENR |= (1<<3) #define LED_G_GPIO_PORT GPIOB #define LED_G_GPIO_PIN GPIO_PIN_0 void delay(uint32_t count) { for(;count!=0;count--); } int main() { GPIO_InitTypeDef GPIO_InitStructure; LED_G_GPIO_CLK_ENABLE; //开启时钟 GPIO_InitStructure.GPIO_Pin = LED_G_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHZ; GPIO_Init(LED_G_GPIO_PORT,&GPIO_InitStructure); while(1) { GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN); delay(0xFFFF); GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN); delay(0xFFFF); } } void SystemInit() { }