STM32基础学习_32单片机学习资料-程序员宅基地

技术标签: stm32  单片机  

P3 串口电路一键下载原理分析

上拉电路

img

三极管

b为基极,c为集电极,e为发射极

作开关使用时,NPN型三极管:b接低电平,则电路截止,b接高电平则电路饱和导通;PNP型三极管:b接高电平,则电路截止,b接低电平则电路饱和导通

GPIO:通用输入输出

P5 初识STM32

PCB打样选择深圳嘉立创公司

 

引脚顺序:从黑点开始逆时针旋转为正方向

 写好的程序编译之后都是一条条指令,存放在 FLASH中,内核要读取这些指令来执行程序就必须通过 ICode 总线

FLASH用来存放程序

SRAM用来存放变量

 (unsigned int*)将地址进行强制类型转换

*+地址单元 可以对该地址里的数据进行操作

 

 该代码指1左移10位,其他都置为0,即第10位置1,其他位都置0

|=  或等于,即第10位为1其他位为0的与原来的相或,然后赋值给原来的置,也就是将第10位置为1,其他位不变

同理,&=,或等于,~为取反,该操作为将第10位取反,其他位不变 

头文件中<>尖括号表示这个头文件不在当前工程的目录下,而是在编译器的目录下

头文件中“”表示这个头文件在当前工程的目录下,如果在当前工程的目录下找不到再去编译器目录下找。

32位寄存器,每个寄存器有32位

P7点亮一个LED灯

#include"stm32f10x.h"


int main (void)
{
	///打开 GPIOB 端口的时钟
	*(unsigned int*)0x40021018 |=(1<<3);
	//配置IO口为输出
	*(unsigned int*)0x40010C00 |= (1<<(4*5));//0x40010C00地址中第一位 置位为1
	//控制 ODR 寄存器
	*(unsigned int*)0x40010C0C &= ~(1<<0);//不能直接0x40010C0C &= ~(1<<0);编译器会把它当做立即数来处理,0x40010C0C第一位 置位为0
}


//置位 |=  ,  清0  &=   

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不要报错
}

P8 GPIO功能框图讲解(重点)

GPIO:general purpose input output,通用输入输出端口

IO端口位的基本结构

第1-4部分为输出,5-7部分为输入

第1部分位保护电路,I/O引脚电压过高时通过上方晶体管进入VDD(3.3V电压),电压过低(负值)时通过下方电路Vss(接地)进行保护

 第2部分位推挽输出电路

为反相器,输入1则输出0,输入0则输出1

 开漏输出:只能输出低电平,不能输出高电平,需要外加上拉电路才能输出高电平

开漏电路一般用在I方C或SM bus总线上

 

第3部分,输出数据寄存器(ODR)能直接控制输出为高电平或低电平,或者通过位设置/清除寄存器(BSRR)来控制输出数据寄存器间接输出高电平或低电平

第4部分, 复用功能输出,使用该功能时第3部分将不起作用,通过片上外设(串口)来控制引脚的输出

第5部分为输入数据寄存器(IDR),能够读取外部引脚的数据,也可以读取ODR中的数据

上拉电路和下拉电路通过端口配置低寄存器(CRL)来设置上拉/下拉输入模式

具体是上拉还是下拉可以通过端口位设置/清除寄存器(BSRR)来通过软件控制

 

肖特基触发器相当于门禁,输入电平大于2.0V时为高电平,输入电平小鱼1.2V时为低电平

 

 第6部分复用功能输入从片上外设输入,

第7部分模拟输入不经过肖特基触发器处理,直接采集模拟信号

直接对内存进行编程

 stm32f10x.h文件中代码

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


//外设 pheriphral

#define PHERIPH_BASE        ((unsigned int)0x40000000)
#define APB1PHERIPH_BASE            PHERIPH_BASE 
#define APB2PHERIPH_BASE           (PHERIPH_BASE + 0x10000)
#define AHBPHERIPH_BASE            (PHERIPH_BASE + 0x20000)


#define RCC_BASE            (AHBPHERIPH_BASE + 0x1000)
#define  GPIOB_BASE         (APB2PHERIPH_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_ODR            *(unsigned int*)(GPIOB_BASE + 0x0C)
#define GPIOB_BSRR            *(unsigned int*)(GPIOB_BASE + 0x10)
#define GPIOB_BRR            *(unsigned int*)(GPIOB_BASE + 0x14)
	

 main,c文件中的代码

#include "stm32f10x.h"


int main (void)
{
#if 0
	///打开 GPIOB 端口的时钟
	*(unsigned int*)0x40021018 |=(1<<3);
	//配置IO口为输出
	*(unsigned int*)0x40010C00 &= ~((0x0f)<<(4*5));//先对该寄存器进行清0
	*(unsigned int*)0x40010C00 |= (1<<(4*5));//0x40010C00地址中第一位 置位为1
	//控制 ODR 寄存器
	*(unsigned int*)0x40010C0C &= ~(1<<5);//不能直接0x40010C0C &= ~(1<<0);编译器会把它当做立即数来处理,0x40010C0C第一位 置位为0
#else
		///打开 GPIOB 端口的时钟
	RCC_APB2ENR  |=(1<<3);
	//配置IO口为输出
	GPIOB_CRL &= ~((0x0f)<<(4*1));
	GPIOB_CRL |= (1<<(4*1));
	//控制 ODR 寄存器来直接控制输出
	//GPIOB_ODR |= (1<<1);
	//控制 BSRR 寄存器来间接控制输出
	GPIOB_BSRR &= ~(1<<1);
#endif
}



//置位 |=  ,  清0  &=   

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不要报错
}

P12 自己写库——构建库函数

采用结构体的方式来控制寄存器

先定义一个结构体(示例中为GPIO_TypeDef),该结构体的成员的类型和分布顺序都和外设的寄存器(示例程序为GPIOB寄存器)的排列方式一样,然后找到外设的基地址,例如程序中的GPIOB_BASE,然后把该地址强制类型转换为GPIO_TypeDef这种结构体类型的指针,这个指针经过强制类型转换将指向该外设的所有寄存器。用结构体加指针即可指向该外设的寄存器。

 

 stm32f10x.h文件中代码

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


//外设 pheriphral

#define PHERIPH_BASE        ((unsigned int)0x40000000)
#define APB1PHERIPH_BASE            PHERIPH_BASE 
#define APB2PHERIPH_BASE           (PHERIPH_BASE + 0x10000)
#define AHBPHERIPH_BASE            (PHERIPH_BASE + 0x20000)


#define RCC_BASE            (AHBPHERIPH_BASE + 0x1000)
#define  GPIOB_BASE         (APB2PHERIPH_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 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_TypeDef;
#define  RCC  ((RCC_TypeDef*)RCC_BASE)//强制类型转换


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)//强制类型转换

 main,c文件中的代码

#include "stm32f10x.h"


int main (void)
{

    ///打开 GPIOB 端口的时钟
	RCC->APB2ENR  |=(1<<3);
	//配置IO口为输出
	GPIOB->CRL &= ~((0x0f)<<(4*1));
	GPIOB->CRL |= (1<<(4*1));
	//控制 ODR 寄存器来直接控制输出
	//GPIOB_ODR |= (1<<1);
	//控制 BSRR 寄存器来间接控制输出
	GPIOB->BSRR &= ~(1<<1);
#endif
}



//置位 |=  ,  清0  &=   

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不要报错
}

}

为了避免头文件被多次调用产生错误警告,在编写头文件的时候开头和结尾加上几个语句便可解决

开头

#ifndef __STM3210X_H//头文件名称为stm32f10x.h,则此处代码对__STM3210X_H进行宏定义
#define __STM3210X_H//同上

结尾

#endif /*__STM3210X_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)0x0001) /*!<选择全部引脚 */ //(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_gpio.h"
void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BSRR |= GPIO_Pin;//控制BSRR寄存器
}

void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BRR |= GPIO_Pin;//控制BRR寄存器
}

 

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

int main (void)
{

	///打开 GPIOB 端口的时钟
	RCC->APB2ENR  |=(1<<3);
	//配置IO口为输出
	GPIOB->CRL &= ~((0x0f)<<(4*0));
	GPIOB->CRL |= (1<<(4*0));
	
	GPIO_SetBits(GPIOB,GPIO_Pin_0);//让PB0端口置位,即调用函数控制将LED关闭
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);//让PB0端口复位,即调用函数将LED打开
#endif
}




//置位 |=  ,  清0  &=   

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不要报错
}

 

#ifndef __STM3210X_H
#define __STM3210X_H
//用来存放STM32寄存器映射的代码


//外设 pheriphral

#define PHERIPH_BASE        ((unsigned int)0x40000000)
#define APB1PHERIPH_BASE            PHERIPH_BASE 
#define APB2PHERIPH_BASE           (PHERIPH_BASE + 0x10000)
#define AHBPHERIPH_BASE            (PHERIPH_BASE + 0x20000)


#define RCC_BASE            (AHBPHERIPH_BASE + 0x1000)
#define  GPIOB_BASE         (APB2PHERIPH_BASE + 0x0C00)

#define RCC_APB2ENR          *(unsigned int*)(RCC_BASE + 0x18)
	
	
typedef unsigned int     uint32_t;
typedef unsigned short   uint16_t;

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_TypeDef;
#define  RCC  ((RCC_TypeDef*)RCC_BASE)

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)


#endif /*__STM3210X_H*/

利用结构体和初始化函数来进行进行初始化

  C语言中变量的定义要放在最开头,不能在程序中间定义,不然会报错

教程中写初始化函数的目的是让程序的可读性更好,不用去查参考手册也能看懂程序要完成什么操作

步骤:定义一个外设的初始化结构体,把能够配置出来的寄存器的所有参数(速度、模式等)全部枚举出来,编程的时候可以把枚举定义的参数写到初始化的结构体里面,最后调用外设的初始化函数,把结构体中配置好的成员写到相应的寄存器里面,实现配置寄存器的工作。

 

#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)0x0001) /*!<选择全部引脚 */ //(11111111 11111111)b

typedef enum//枚举
{
	GPIO_Speed_10MHZ = 1,       //10 MHZ     (01)b
	GPIO_Speed_2MHZ,            //2MHZ       (10)b
	GPIO_Speed_50MHZ            //50MHZ      (11)b
}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;//控制BSRR寄存器
}

void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
{
	GPIOx->BRR |= GPIO_Pin;//控制BRR寄存器
}

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)
{

	GPIO_InitTypeDef GPIO_InitStructure;//  C语言中变量的定义要放在最开头,不能在程序中间定义,不然会报错
		///打开 GPIOB 端口的时钟
	RCC->APB2ENR  |=(1<<3);
	//配置IO口为输出
  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	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_0);//调用函数控制将LED关闭
	GPIO_ResetBits(GPIOB,GPIO_Pin_0);//调用函数将LED打开
#endif
}




//置位 |=  ,  清0  &=   

void SystemInit(void)
{
	//函数体为空,目的是为了骗过编译器不要报错
}

#ifndef __STM3210X_H
#define __STM3210X_H
//用来存放STM32寄存器映射的代码


//外设 pheriphral

#define PHERIPH_BASE        ((unsigned int)0x40000000)
#define APB1PHERIPH_BASE            PHERIPH_BASE 
#define APB2PHERIPH_BASE           (PHERIPH_BASE + 0x10000)
#define AHBPHERIPH_BASE            (PHERIPH_BASE + 0x20000)


#define RCC_BASE            (AHBPHERIPH_BASE + 0x1000)
#define  GPIOB_BASE         (APB2PHERIPH_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 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_TypeDef;
#define  RCC  ((RCC_TypeDef*)RCC_BASE)

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)


#endif /*__STM3210X_H*/

P10 固件库编程

固件库包含哪些文件

1-汇编编写的启动文件
startup_stm32f10x_hd.s:设置堆栈指针、设置PC指针、初始化中断向量表、配置系统时钟、对用C库函数_main最终去到C的世界

2-时钟配置文件
system_stm32f10x.c:把外部时钟HSE=8M,经过PLL倍频为72M。

3-外设相关的
stm32f10x.h:实现了内核之外的外设的寄存器映射
xxx:GPIO、USRAT、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单片机中的  #include <reg51.h>

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

P11 新建工程

新建工程时如果不用设置好的模板,要先配置一下环境

每新建一个头文件都要指定路径

为了避免头文件被多次调用产生错误警告,在编写头文件的时候开头和结尾加上几个语句便可解决

例如对于头文件

开头

#ifndef _BSP_LED_H//起名为_BSP_LED_H为行业习惯是按照这种格式起名,也可以用来区分自己写的宏,其实起什么名字都可以
#define _BSP_LED_H

结尾

#endif /*_BSP_LED_H*/

这样只有在bsp_led.h文件没有编译的时候才会编译该文件,如果已经编译过了就会跳过不执行

P12 GPIO输出-使用固件库点亮LED

开时钟函数要放在配置函数之前,不然灯亮不了

P13 GPIO输入-按键检测

输入的是上升沿电平

电容来进行消除抖动(按键时有20ms左右的抖动),为硬件消抖,软件上就不需要消抖了

1k电阻为保护电阻,减小电流,防止引脚击穿

P14 位带操作

 一个地址对应一个字节,一个字节有8位

 

P15 启动文件讲解

中断优先级数值越小,优先级越高

如果两个中断设置的软件优先级相同,那么看二者硬件编号哪个小,小的优先级高

写中断服务函数时,函数名一定要和这个向量表表里的函数一样 

P16 RCC时钟

可以通过读取MCO时钟输出来检测SYSCLK系统时钟配置是否正确

 

 

        首先使用HSE(8MHz)(高速外部时钟)来配置系统时钟, 不分频经过PLLXTPRE,然后进入锁相环时钟源配置PLLSRC这个位,然后进入锁相环倍频因子,配置成9倍频,得出锁相环时钟PLLCLK为72MHz,配置SYSCLK系统时钟时面临三个选择:HSI/HSE/PLLCLK,选择锁相环时钟等于系统时钟(72MHz)。

        接下来配置三条总线的分频因子,AHB高速总线配置1分频,出来的AHB时钟为72MHz,然后进过APB1和APB2的预分频因子,APB1因为最大为36MHz,所以选择2分频,APB1为高速总线,最大为72MHz,所以配置成1分频。那么总线已经配置好了使用外设时再来配置外设的分频因子。

时钟安全系统CSS

如果没有使能CSS中断的话,系统会在硬件上把时钟切换到HSI上,也就是8MHz,用户需要通过在软件上选择HSI间接控制PLL来产生64MHz(8/2*16)的时钟,有的外设还能使用,但是最保险的还是报警或通知用户有故障产生。 

P17 中断应用总结

先比较主优先级,再比较子优先级,谁小谁优先级高,如果都一样则比较硬件中断编号(中断向量表中可查),谁小谁优先级高

 

 NVIC中的中断使能为总开关,外设的中断使能为小开关,只有总开关和使用的外设的开关都打开才能使用该外设的中断。

中断源在stm32f10x.h文件里的IRQn_Type结构体里面找

中断服务函数的名称要和启动文件startup_stm32f10x_hd.s里向量表里定义的中断服务函数名称一样

中断服务函数都放在stm32f10x_it.c里,便于统一管理和分配优先级

P18 EXTI(外部中断/事件控制器)

GPIO要产生中断一定要读取一个电平,电平有上升沿或下降沿,这两种信号通过EXTI产生中断,然后交给内核NVIC。即先到GPIO再到EXTI再到NVIC三步。

NVIC的固件库文件在misc.h中

触发事件: 脉冲发生器在单片机内部,触发后产生一个脉冲信号,可以用来控制ADC开始采集或者定时器开始计数等事件。

P19 SysTick 系统定时器

P20 通信的基本概念 

 同步与异步:有时钟信号的都叫同步,没有时钟信号的都叫异步

 

P21 串口通信

 

从单片机或者芯片中出来的都叫TTL电平

RS232:逻辑1为-15V,逻辑0为+15V

 

stm32串口功能框图讲解

 n表示低电平有效

 注意串口1是挂在APB2总线上的,串口2、3、4、5是挂载到APB1总线上的,编程的时候要注意修改时钟

 串口是全双工通讯

 APB1为低速时钟,为36M

 USART_BRR寄存器分为整数部分和小数部分,小数部分为四位,最小分辨率为1/16=0.065.

 串口发送都是一个字节一个字节的发,即8位8位的发

多个字节或数组或字符串发送要用循环语句

P22 DMA

M指Memory,即寄存器,P指Peripheral,即外设

 DMA2只存在于大容量和互联型的产品中

 Memory to Memory模式可以访问DMA1、DMA2的任意通道

P23 存储器

RAM(random access memory)即随机存储器:掉电数据会丢失,但读写速度较快

ROM(Read-Only Memory)即只读存储器:掉电后数据不会丢失,但读写速度较慢

 

P24 I2C

24.1物理层

 当设备空闲时输出高阻态可以防止对其他设备产生干扰

当输出逻辑1时也是高阻态,想当于是将主机直接连接到上拉电阻,利用上拉电阻的电压来产生高电平(如图所示)

 从机通过输出低电平来将总线拉成低电平,来表示该从机正在占用总线,其他设备就不要再用了。

开漏输出就是不输出电压,控制输出低电平时引脚接地,控制输出高电平时引脚既不输出高电平,也不输出低电平,为高阻态。如果外接上拉电阻,则在输出高电平时电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机电压低的时候。

24.2协议层

 通讯复合格式可以读某个寄存器中的某个位

 

7位的设备地址指的是从机的地址,8位的设备地址指的是从机地址加上“读/写”的一位

 发送端释放SDA控制权时,若数据接收端为高电平,则停止数据传输,若数据接收端为低电平,则继续传输

PCLK1是指APB1的时钟

P25 SPI——读写串行FLASH

就算只是从机读取数据也要让主机给从机发送数据才能执行操作,因为只有主机发送了数据,SCK时钟才会开启

norflash写入时就没有大小的限制了,可以一个字节一个字节的写入 ,不需要一个扇区一个扇区的写入

 

或等于“|=”想当于相加,与等于“&=”相当于其本身(但可以用来取多字节的某个字节,比如32位数据0xaabbccdd与8位数据0xff进行&操作,可以得到其最低8位的数据,即0xdd)

判断数据是否溢出(超出栈的最大空间),可以通过在stm32f10x_it.c文件中的HardFalut_Handler函数处设置断点进行调试,若程序卡死在这里则表示确实是溢出造成的错误,可通过将数组定义在函数外,即定义为全局变量来解决问题,全局变量使用的不是栈空间,而是内存的空余的空间。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_44245207/article/details/122411319

智能推荐

EasyDarwin开源流媒体云平台之EasyRMS录播服务器功能设计_开源录播系统-程序员宅基地

文章浏览阅读3.6k次。需求背景EasyDarwin开发团队维护EasyDarwin开源流媒体服务器也已经很多年了,之前也陆陆续续尝试过很多种服务端录像的方案,有:在EasyDarwin中直接解析收到的RTP包,重新组包录像;也有:在EasyDarwin中新增一个RecordModule,再以RTSPClient的方式请求127.0.0.1自己的直播流录像,但这些始终都没有成气候;我们的想法是能够让整套EasyDarwin_开源录播系统

oracle Plsql 执行update或者delete时卡死问题解决办法_oracle delete update 锁表问题-程序员宅基地

文章浏览阅读1.1w次。今天碰到一个执行语句等了半天没有执行:delete table XXX where ......,但是在select 的时候没问题。后来发现是在执行select * from XXX for update 的时候没有commit,oracle将该记录锁住了。可以通过以下办法解决: 先查询锁定记录 Sql代码 SELECT s.sid, s.seri_oracle delete update 锁表问题

Xcode Undefined symbols 错误_xcode undefined symbols:-程序员宅基地

文章浏览阅读3.4k次。报错信息error:Undefined symbol: typeinfo for sdk::IConfigUndefined symbol: vtable for sdk::IConfig具体信息:Undefined symbols for architecture x86_64: "typeinfo for sdk::IConfig", referenced from: typeinfo for sdk::ConfigImpl in sdk.a(config_impl.o) _xcode undefined symbols:

项目05(Mysql升级07Mysql5.7.32升级到Mysql8.0.22)_mysql8.0.26 升级32-程序员宅基地

文章浏览阅读249次。背景《承接上文,项目05(Mysql升级06Mysql5.6.51升级到Mysql5.7.32)》,写在前面需要(考虑)检查和测试的层面很多,不限于以下内容。参考文档https://dev.mysql.com/doc/refman/8.0/en/upgrade-prerequisites.htmllink推荐阅读以上链接,因为对应以下问题,有详细的建议。官方文档:不得存在以下问题:0.不得有使用过时数据类型或功能的表。不支持就地升级到MySQL 8.0,如果表包含在预5.6.4格_mysql8.0.26 升级32

高通编译8155源码环境搭建_高通8155 qnx 源码-程序员宅基地

文章浏览阅读3.7k次。一.安装基本环境工具:1.安装git工具sudo apt install wget g++ git2.检查并安装java等环境工具2.1、执行下面安装命令#!/bin/bashsudoapt-get-yinstall--upgraderarunrarsudoapt-get-yinstall--upgradepython-pippython3-pip#aliyunsudoapt-get-yinstall--upgradeopenjdk..._高通8155 qnx 源码

firebase 与谷歌_Firebase的好与不好-程序员宅基地

文章浏览阅读461次。firebase 与谷歌 大多数开发人员都听说过Google的Firebase产品。 这就是Google所说的“ 移动平台,可帮助您快速开发高质量的应用程序并发展业务。 ”。 它基本上是大多数开发人员在构建应用程序时所需的一组工具。 在本文中,我将介绍这些工具,并指出您选择使用Firebase时需要了解的所有内容。 在开始之前,我需要说的是,我不会详细介绍Firebase提供的所有工具。 我..._firsebase 与 google

随便推点

k8s挂载目录_kubernetes(k8s)的pod使用统一的配置文件configmap挂载-程序员宅基地

文章浏览阅读1.2k次。在容器化应用中,每个环境都要独立的打一个镜像再给镜像一个特有的tag,这很麻烦,这就要用到k8s原生的配置中心configMap就是用解决这个问题的。使用configMap部署应用。这里使用nginx来做示例,简单粗暴。直接用vim常见nginx的配置文件,用命令导入进去kubectl create cm nginx.conf --from-file=/home/nginx.conf然后查看kub..._pod mount目录会自动创建吗

java计算机毕业设计springcloud+vue基于微服务的分布式新生报到系统_关于spring cloud的参考文献有啥-程序员宅基地

文章浏览阅读169次。随着互联网技术的发发展,计算机技术广泛应用在人们的生活中,逐渐成为日常工作、生活不可或缺的工具,高校各种管理系统层出不穷。高校作为学习知识和技术的高等学府,信息技术更加的成熟,为新生报到管理开发必要的系统,能够有效的提升管理效率。一直以来,新生报到一直没有进行系统化的管理,学生无法准确查询学院信息,高校也无法记录新生报名情况,由此提出开发基于微服务的分布式新生报到系统,管理报名信息,学生可以在线查询报名状态,节省时间,提高效率。_关于spring cloud的参考文献有啥

VB.net学习笔记(十五)继承与多接口练习_vb.net 继承多个接口-程序员宅基地

文章浏览阅读3.2k次。Public MustInherit Class Contact '只能作基类且不能实例化 Private mID As Guid = Guid.NewGuid Private mName As String Public Property ID() As Guid Get Return mID End Get_vb.net 继承多个接口

【Nexus3】使用-Nexus3批量上传jar包 artifact upload_nexus3 批量上传jar包 java代码-程序员宅基地

文章浏览阅读1.7k次。1.美图# 2.概述因为要上传我的所有仓库的包,希望nexus中已有的包,我不覆盖,没有的添加。所以想批量上传jar。3.方案1-脚本批量上传PS:nexus3.x版本只能通过脚本上传3.1 批量放入jar在mac目录下,新建一个文件夹repo,批量放入我们需要的本地库文件夹,并对文件夹授权(base) lcc@lcc nexus-3.22.0-02$ mkdir repo2..._nexus3 批量上传jar包 java代码

关于去隔行的一些概念_mipi去隔行-程序员宅基地

文章浏览阅读6.6k次,点赞6次,收藏30次。本文转自http://blog.csdn.net/charleslei/article/details/486519531、什么是场在介绍Deinterlacer去隔行处理的方法之前,我们有必要提一下关于交错场和去隔行处理的基本知识。那么什么是场呢,场存在于隔行扫描记录的视频中,隔行扫描视频的每帧画面均包含两个场,每一个场又分别含有该帧画面的奇数行扫描线或偶数行扫描线信息,_mipi去隔行

ABAP自定义Search help_abap 自定义 search help-程序员宅基地

文章浏览阅读1.7k次。DATA L_ENDDA TYPE SY-DATUM. IF P_DATE IS INITIAL. CONCATENATE SY-DATUM(4) '1231' INTO L_ENDDA. ELSE. CONCATENATE P_DATE(4) '1231' INTO L_ENDDA. ENDIF. DATA: LV_RESET(1) TY_abap 自定义 search help