当前位置:   article > 正文

STM32基础学习_32单片机学习资料

32单片机学习资料

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灯

  1. #include"stm32f10x.h"
  2. int main (void)
  3. {
  4. ///打开 GPIOB 端口的时钟
  5. *(unsigned int*)0x40021018 |=(1<<3);
  6. //配置IO口为输出
  7. *(unsigned int*)0x40010C00 |= (1<<(4*5));//0x40010C00地址中第一位 置位为1
  8. //控制 ODR 寄存器
  9. *(unsigned int*)0x40010C0C &= ~(1<<0);//不能直接0x40010C0C &= ~(1<<0);编译器会把它当做立即数来处理,0x40010C0C第一位 置位为0
  10. }
  11. //置位 |= , 清0 &=
  12. void SystemInit(void)
  13. {
  14. //函数体为空,目的是为了骗过编译器不要报错
  15. }

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文件中代码

  1. //用来存放STM32寄存器映射的代码
  2. //外设 pheriphral
  3. #define PHERIPH_BASE ((unsigned int)0x40000000)
  4. #define APB1PHERIPH_BASE PHERIPH_BASE
  5. #define APB2PHERIPH_BASE (PHERIPH_BASE + 0x10000)
  6. #define AHBPHERIPH_BASE (PHERIPH_BASE + 0x20000)
  7. #define RCC_BASE (AHBPHERIPH_BASE + 0x1000)
  8. #define GPIOB_BASE (APB2PHERIPH_BASE + 0x0C00)
  9. #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
  10. #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
  11. #define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
  12. #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
  13. #define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE + 0x10)
  14. #define GPIOB_BRR *(unsigned int*)(GPIOB_BASE + 0x14)

 main,c文件中的代码

  1. #include "stm32f10x.h"
  2. int main (void)
  3. {
  4. #if 0
  5. ///打开 GPIOB 端口的时钟
  6. *(unsigned int*)0x40021018 |=(1<<3);
  7. //配置IO口为输出
  8. *(unsigned int*)0x40010C00 &= ~((0x0f)<<(4*5));//先对该寄存器进行清0
  9. *(unsigned int*)0x40010C00 |= (1<<(4*5));//0x40010C00地址中第一位 置位为1
  10. //控制 ODR 寄存器
  11. *(unsigned int*)0x40010C0C &= ~(1<<5);//不能直接0x40010C0C &= ~(1<<0);编译器会把它当做立即数来处理,0x40010C0C第一位 置位为0
  12. #else
  13. ///打开 GPIOB 端口的时钟
  14. RCC_APB2ENR |=(1<<3);
  15. //配置IO口为输出
  16. GPIOB_CRL &= ~((0x0f)<<(4*1));
  17. GPIOB_CRL |= (1<<(4*1));
  18. //控制 ODR 寄存器来直接控制输出
  19. //GPIOB_ODR |= (1<<1);
  20. //控制 BSRR 寄存器来间接控制输出
  21. GPIOB_BSRR &= ~(1<<1);
  22. #endif
  23. }
  24. //置位 |= , 清0 &=
  25. void SystemInit(void)
  26. {
  27. //函数体为空,目的是为了骗过编译器不要报错
  28. }

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

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

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

 

 stm32f10x.h文件中代码

  1. //用来存放STM32寄存器映射的代码
  2. //外设 pheriphral
  3. #define PHERIPH_BASE ((unsigned int)0x40000000)
  4. #define APB1PHERIPH_BASE PHERIPH_BASE
  5. #define APB2PHERIPH_BASE (PHERIPH_BASE + 0x10000)
  6. #define AHBPHERIPH_BASE (PHERIPH_BASE + 0x20000)
  7. #define RCC_BASE (AHBPHERIPH_BASE + 0x1000)
  8. #define GPIOB_BASE (APB2PHERIPH_BASE + 0x0C00)
  9. #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
  10. //#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
  11. //#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
  12. //#define GPIOB_IDR *(unsigned int*)(GPIOB_BASE + 0x08)
  13. //#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
  14. //#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE + 0x10)
  15. //#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE + 0x14)
  16. //#define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE + 0x18)
  17. typedef unsigned int uint32_t;
  18. typedef unsigned short uint16_t;
  19. typedef struct
  20. {
  21. uint32_t CR;
  22. uint32_t CFGR;
  23. uint32_t CIR;
  24. uint32_t APB2RSTR;
  25. uint32_t APB1RSTR;
  26. uint32_t AHBENR;
  27. uint32_t APB2ENR;
  28. uint32_t APB1ENR;
  29. uint32_t BDCR;
  30. uint32_t CSR;
  31. }RCC_TypeDef;
  32. #define RCC ((RCC_TypeDef*)RCC_BASE)//强制类型转换
  33. typedef struct
  34. {
  35. uint32_t CRL;
  36. uint32_t CRH;
  37. uint32_t IDR;
  38. uint32_t ODR;
  39. uint32_t BSRR;
  40. uint32_t BRR;
  41. uint32_t LCKR;
  42. }GPIO_TypeDef;
  43. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)//强制类型转换

 main,c文件中的代码

  1. #include "stm32f10x.h"
  2. int main (void)
  3. {
  4. ///打开 GPIOB 端口的时钟
  5. RCC->APB2ENR |=(1<<3);
  6. //配置IO口为输出
  7. GPIOB->CRL &= ~((0x0f)<<(4*1));
  8. GPIOB->CRL |= (1<<(4*1));
  9. //控制 ODR 寄存器来直接控制输出
  10. //GPIOB_ODR |= (1<<1);
  11. //控制 BSRR 寄存器来间接控制输出
  12. GPIOB->BSRR &= ~(1<<1);
  13. #endif
  14. }
  15. //置位 |= , 清0 &=
  16. void SystemInit(void)
  17. {
  18. //函数体为空,目的是为了骗过编译器不要报错
  19. }
  20. }

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

开头

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

结尾

#endif /*__STM3210X_H*/

定义函数来控制寄存器

增加程序的可读性,定义了端口置位和复位函数来控制端口的输出

 

  1. #ifndef __STM32F10X_GPIO_H
  2. #define __STM32F10X_GPIO_H
  3. #include "stm32f10x.h"
  4. #define GPIO_Pin_0 ((uint16_t)0x0001) /*!<选择Pin0 */ //(00000000 00000001)b
  5. #define GPIO_Pin_1 ((uint16_t)0x0002) /*!<选择Pin1 */ //(00000000 00000010)b
  6. #define GPIO_Pin_2 ((uint16_t)0x0004) /*!<选择Pin2 */ //(00000000 00000100)b
  7. #define GPIO_Pin_3 ((uint16_t)0x0008) /*!<选择Pin3 */ //(00000000 00001000)b
  8. #define GPIO_Pin_4 ((uint16_t)0x0010) /*!<选择Pin4 */ //(00000000 00010000)b
  9. #define GPIO_Pin_5 ((uint16_t)0x0020) /*!<选择Pin5 */ //(00000000 00100000)b
  10. #define GPIO_Pin_6 ((uint16_t)0x0040) /*!<选择Pin6 */ //(00000000 01000000)b
  11. #define GPIO_Pin_7 ((uint16_t)0x0080) /*!<选择Pin7 */ //(00000000 10000000)b
  12. #define GPIO_Pin_8 ((uint16_t)0x0100) /*!<选择Pin8 */ //(00000001 00000000)b
  13. #define GPIO_Pin_9 ((uint16_t)0x0200) /*!<选择Pin9 */ //(00000010 00000000)b
  14. #define GPIO_Pin_10 ((uint16_t)0x0400) /*!<选择Pin10 */ //(00000100 00000000)b
  15. #define GPIO_Pin_11 ((uint16_t)0x0800) /*!<选择Pin11 */ //(00001000 00000000)b
  16. #define GPIO_Pin_12 ((uint16_t)0x1000) /*!<选择Pin12 */ //(00010000 00000000)b
  17. #define GPIO_Pin_13 ((uint16_t)0x2000) /*!<选择Pin13 */ //(00100000 00000000)b
  18. #define GPIO_Pin_14 ((uint16_t)0x4000) /*!<选择Pin14 */ //(01000000 00000000)b
  19. #define GPIO_Pin_15 ((uint16_t)0x8000) /*!<选择Pin15 */ //(10000000 00000000)b
  20. #define GPIO_Pin_All ((uint16_t)0x0001) /*!<选择全部引脚 */ //(11111111 11111111)b
  21. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  22. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  23. #endif /* __STM32F10X_GPIO_H*/

 

  1. #include "stm32f10x_gpio.h"
  2. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  3. {
  4. GPIOx->BSRR |= GPIO_Pin;//控制BSRR寄存器
  5. }
  6. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  7. {
  8. GPIOx->BRR |= GPIO_Pin;//控制BRR寄存器
  9. }

 

  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"
  3. int main (void)
  4. {
  5. ///打开 GPIOB 端口的时钟
  6. RCC->APB2ENR |=(1<<3);
  7. //配置IO口为输出
  8. GPIOB->CRL &= ~((0x0f)<<(4*0));
  9. GPIOB->CRL |= (1<<(4*0));
  10. GPIO_SetBits(GPIOB,GPIO_Pin_0);//让PB0端口置位,即调用函数控制将LED关闭
  11. GPIO_ResetBits(GPIOB,GPIO_Pin_0);//让PB0端口复位,即调用函数将LED打开
  12. #endif
  13. }
  14. //置位 |= , 清0 &=
  15. void SystemInit(void)
  16. {
  17. //函数体为空,目的是为了骗过编译器不要报错
  18. }

 

  1. #ifndef __STM3210X_H
  2. #define __STM3210X_H
  3. //用来存放STM32寄存器映射的代码
  4. //外设 pheriphral
  5. #define PHERIPH_BASE ((unsigned int)0x40000000)
  6. #define APB1PHERIPH_BASE PHERIPH_BASE
  7. #define APB2PHERIPH_BASE (PHERIPH_BASE + 0x10000)
  8. #define AHBPHERIPH_BASE (PHERIPH_BASE + 0x20000)
  9. #define RCC_BASE (AHBPHERIPH_BASE + 0x1000)
  10. #define GPIOB_BASE (APB2PHERIPH_BASE + 0x0C00)
  11. #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
  12. typedef unsigned int uint32_t;
  13. typedef unsigned short uint16_t;
  14. typedef struct
  15. {
  16. uint32_t CR;
  17. uint32_t CFGR;
  18. uint32_t CIR;
  19. uint32_t APB2RSTR;
  20. uint32_t APB1RSTR;
  21. uint32_t AHBENR;
  22. uint32_t APB2ENR;
  23. uint32_t APB1ENR;
  24. uint32_t BDCR;
  25. uint32_t CSR;
  26. }RCC_TypeDef;
  27. #define RCC ((RCC_TypeDef*)RCC_BASE)
  28. typedef struct
  29. {
  30. uint32_t CRL;
  31. uint32_t CRH;
  32. uint32_t IDR;
  33. uint32_t ODR;
  34. uint32_t BSRR;
  35. uint32_t BRR;
  36. uint32_t LCKR;
  37. }GPIO_TypeDef;
  38. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
  39. #endif /*__STM3210X_H*/

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

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

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

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

 

  1. #ifndef __STM32F10X_GPIO_H
  2. #define __STM32F10X_GPIO_H
  3. #include "stm32f10x.h"
  4. #define GPIO_Pin_0 ((uint16_t)0x0001) /*!<选择Pin0 */ //(00000000 00000001)b
  5. #define GPIO_Pin_1 ((uint16_t)0x0002) /*!<选择Pin1 */ //(00000000 00000010)b
  6. #define GPIO_Pin_2 ((uint16_t)0x0004) /*!<选择Pin2 */ //(00000000 00000100)b
  7. #define GPIO_Pin_3 ((uint16_t)0x0008) /*!<选择Pin3 */ //(00000000 00001000)b
  8. #define GPIO_Pin_4 ((uint16_t)0x0010) /*!<选择Pin4 */ //(00000000 00010000)b
  9. #define GPIO_Pin_5 ((uint16_t)0x0020) /*!<选择Pin5 */ //(00000000 00100000)b
  10. #define GPIO_Pin_6 ((uint16_t)0x0040) /*!<选择Pin6 */ //(00000000 01000000)b
  11. #define GPIO_Pin_7 ((uint16_t)0x0080) /*!<选择Pin7 */ //(00000000 10000000)b
  12. #define GPIO_Pin_8 ((uint16_t)0x0100) /*!<选择Pin8 */ //(00000001 00000000)b
  13. #define GPIO_Pin_9 ((uint16_t)0x0200) /*!<选择Pin9 */ //(00000010 00000000)b
  14. #define GPIO_Pin_10 ((uint16_t)0x0400) /*!<选择Pin10 */ //(00000100 00000000)b
  15. #define GPIO_Pin_11 ((uint16_t)0x0800) /*!<选择Pin11 */ //(00001000 00000000)b
  16. #define GPIO_Pin_12 ((uint16_t)0x1000) /*!<选择Pin12 */ //(00010000 00000000)b
  17. #define GPIO_Pin_13 ((uint16_t)0x2000) /*!<选择Pin13 */ //(00100000 00000000)b
  18. #define GPIO_Pin_14 ((uint16_t)0x4000) /*!<选择Pin14 */ //(01000000 00000000)b
  19. #define GPIO_Pin_15 ((uint16_t)0x8000) /*!<选择Pin15 */ //(10000000 00000000)b
  20. #define GPIO_Pin_All ((uint16_t)0x0001) /*!<选择全部引脚 */ //(11111111 11111111)b
  21. typedef enum//枚举
  22. {
  23. GPIO_Speed_10MHZ = 1, //10 MHZ (01)b
  24. GPIO_Speed_2MHZ, //2MHZ (10)b
  25. GPIO_Speed_50MHZ //50MHZ (11)b
  26. }GPIOSpeed_TypeDef;
  27. typedef enum
  28. {
  29. GPIO_Mode_AIN = 0x0, //模拟输入 (0000 0000)b
  30. GPIO_Mode_IN_FLOATING = 0x04, //浮空输入 (0000 0100)b
  31. GPIO_Mode_IPD = 0x28, //下拉输入 (0010 1000)b
  32. GPIO_Mode_IPU = 0x48, //上拉输入 (0100 1000)b
  33. GPIO_Mode_Out_OD = 0x14, //开漏输出 (0001 0100)b
  34. GPIO_Mode_Out_PP = 0x10, //推挽输出 (0001 0000)b
  35. GPIO_Mode_AF_OD = 0x1C, //复用开漏输出 (0001 1100)b
  36. GPIO_Mode_AF_PP = 0x18, //复用推挽输出 (0001 1000)b
  37. }GPIOMode_TypeDef;
  38. typedef struct
  39. {
  40. uint16_t GPIO_Pin;
  41. uint16_t GPIO_Speed;
  42. uint16_t GPIO_Mode;
  43. }GPIO_InitTypeDef;
  44. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  45. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);
  46. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
  47. #endif /* __STM32F10X_GPIO_H*/

  1. #include "stm32f10x_gpio.h"
  2. void GPIO_SetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  3. {
  4. GPIOx->BSRR |= GPIO_Pin;//控制BSRR寄存器
  5. }
  6. void GPIO_ResetBits(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)
  7. {
  8. GPIOx->BRR |= GPIO_Pin;//控制BRR寄存器
  9. }
  10. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
  11. {
  12. uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  13. uint32_t tmpreg = 0x00, pinmask = 0x00;
  14. /*---------------------- GPIO 模式配置 --------------------------*/
  15. // 把输入参数GPIO_Mode的低四位暂存在currentmode
  16. currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  17. // bit4是1表示输出,bit4是0则是输入
  18. // 判断bit4是1还是0,即首选判断是输入还是输出模式
  19. if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  20. {
  21. // 输出模式则要设置输出速度
  22. currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  23. }
  24. /*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  25. // 配置端口低8位,即Pin0~Pin7
  26. if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  27. {
  28. // 先备份CRL寄存器的值
  29. tmpreg = GPIOx->CRL;
  30. // 循环,从Pin0开始配对,找出具体的Pin
  31. for (pinpos = 0x00; pinpos < 0x08; pinpos++)
  32. {
  33. // pos的值为1左移pinpos位
  34. pos = ((uint32_t)0x01) << pinpos;
  35. // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
  36. currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
  37. //若currentpin=pos,则找到使用的引脚
  38. if (currentpin == pos)
  39. {
  40. // pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
  41. pos = pinpos << 2;
  42. //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
  43. pinmask = ((uint32_t)0x0F) << pos;
  44. tmpreg &= ~pinmask;
  45. // 向寄存器写入将要配置的引脚的模式
  46. tmpreg |= (currentmode << pos);
  47. // 判断是否为下拉输入模式
  48. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  49. {
  50. // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
  51. GPIOx->BRR = (((uint32_t)0x01) << pinpos);
  52. }
  53. else
  54. {
  55. // 判断是否为上拉输入模式
  56. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  57. {
  58. // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
  59. GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
  60. }
  61. }
  62. }
  63. }
  64. // 把前面处理后的暂存值写入到CRL寄存器之中
  65. GPIOx->CRL = tmpreg;
  66. }
  67. /*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  68. // 配置端口高8位,即Pin8~Pin15
  69. if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  70. {
  71. // // 先备份CRH寄存器的值
  72. tmpreg = GPIOx->CRH;
  73. // 循环,从Pin8开始配对,找出具体的Pin
  74. for (pinpos = 0x00; pinpos < 0x08; pinpos++)
  75. {
  76. pos = (((uint32_t)0x01) << (pinpos + 0x08));
  77. // pos与输入参数GPIO_PIN作位与运算
  78. currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
  79. //若currentpin=pos,则找到使用的引脚
  80. if (currentpin == pos)
  81. {
  82. //pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
  83. pos = pinpos << 2;
  84. //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
  85. pinmask = ((uint32_t)0x0F) << pos;
  86. tmpreg &= ~pinmask;
  87. // 向寄存器写入将要配置的引脚的模式
  88. tmpreg |= (currentmode << pos);
  89. // 判断是否为下拉输入模式
  90. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
  91. {
  92. // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
  93. GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
  94. }
  95. // 判断是否为上拉输入模式
  96. if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
  97. {
  98. // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
  99. GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
  100. }
  101. }
  102. }
  103. // 把前面处理后的暂存值写入到CRH寄存器之中
  104. GPIOx->CRH = tmpreg;
  105. }
  106. }

  1. #include "stm32f10x.h"
  2. #include "stm32f10x_gpio.h"
  3. int main (void)
  4. {
  5. GPIO_InitTypeDef GPIO_InitStructure;//  C语言中变量的定义要放在最开头,不能在程序中间定义,不然会报错
  6. ///打开 GPIOB 端口的时钟
  7. RCC->APB2ENR |=(1<<3);
  8. //配置IO口为输出
  9. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHZ;
  12. GPIO_Init(GPIOB, &GPIO_InitStructure);//&为取地址符,函数定义时此处为结构体的地址,故使用函数时这里调用地址
  13. GPIO_SetBits(GPIOB,GPIO_Pin_0);//调用函数控制将LED关闭
  14. GPIO_ResetBits(GPIOB,GPIO_Pin_0);//调用函数将LED打开
  15. #endif
  16. }
  17. //置位 |= , 清0 &=
  18. void SystemInit(void)
  19. {
  20. //函数体为空,目的是为了骗过编译器不要报错
  21. }

  1. #ifndef __STM3210X_H
  2. #define __STM3210X_H
  3. //用来存放STM32寄存器映射的代码
  4. //外设 pheriphral
  5. #define PHERIPH_BASE ((unsigned int)0x40000000)
  6. #define APB1PHERIPH_BASE PHERIPH_BASE
  7. #define APB2PHERIPH_BASE (PHERIPH_BASE + 0x10000)
  8. #define AHBPHERIPH_BASE (PHERIPH_BASE + 0x20000)
  9. #define RCC_BASE (AHBPHERIPH_BASE + 0x1000)
  10. #define GPIOB_BASE (APB2PHERIPH_BASE + 0x0C00)
  11. #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
  12. //#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
  13. //#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
  14. //#define GPIOB_IDR *(unsigned int*)(GPIOB_BASE + 0x08)
  15. //#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
  16. //#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE + 0x10)
  17. //#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE + 0x14)
  18. //#define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE + 0x18)
  19. typedef unsigned int uint32_t;
  20. typedef unsigned short uint16_t;
  21. typedef struct
  22. {
  23. uint32_t CR;
  24. uint32_t CFGR;
  25. uint32_t CIR;
  26. uint32_t APB2RSTR;
  27. uint32_t APB1RSTR;
  28. uint32_t AHBENR;
  29. uint32_t APB2ENR;
  30. uint32_t APB1ENR;
  31. uint32_t BDCR;
  32. uint32_t CSR;
  33. }RCC_TypeDef;
  34. #define RCC ((RCC_TypeDef*)RCC_BASE)
  35. typedef struct
  36. {
  37. uint32_t CRL;
  38. uint32_t CRH;
  39. uint32_t IDR;
  40. uint32_t ODR;
  41. uint32_t BSRR;
  42. uint32_t BRR;
  43. uint32_t LCKR;
  44. }GPIO_TypeDef;
  45. #define GPIOB ((GPIO_TypeDef*)GPIOB_BASE)
  46. #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,当中断较多时单独放在一个文件里可以便于查看中断并分配优先级。

  1. #include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
  2. int main(void)
  3. {
  4.     // 来到这里的时候,系统的时钟已经被配置成72M。
  5. }

P11 新建工程

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

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

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

例如对于头文件

开头

  1. #ifndef _BSP_LED_H//起名为_BSP_LED_H为行业习惯是按照这种格式起名,也可以用来区分自己写的宏,其实起什么名字都可以
  2. #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函数处设置断点进行调试,若程序卡死在这里则表示确实是溢出造成的错误,可通过将数组定义在函数外,即定义为全局变量来解决问题,全局变量使用的不是栈空间,而是内存的空余的空间。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/482224
推荐阅读
相关标签
  

闽ICP备14008679号