打印
[其他ST产品]

基于指南者STM32_入门

[复制链接]
楼主: 喂什么玩意
手机看帖
扫描二维码
随时随地手机跟帖
21
喂什么玩意|  楼主 | 2024-3-31 21:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
C语言对寄存器的封装
总线和外设基址宏定义

使用特权

评论回复
22
喂什么玩意|  楼主 | 2024-3-31 21:02 | 只看该作者
让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);

使用特权

评论回复
23
喂什么玩意|  楼主 | 2024-3-31 21:02 | 只看该作者
使用结构体封装寄存器列表

使用特权

评论回复
24
喂什么玩意|  楼主 | 2024-3-31 21:03 | 只看该作者
使用结构体指针访问寄存器

使用特权

评论回复
25
喂什么玩意|  楼主 | 2024-3-31 21:03 | 只看该作者
定义GPIO端口基地址指针

使用特权

评论回复
26
喂什么玩意|  楼主 | 2024-3-31 21:03 | 只看该作者
新建工程-寄存器版本
创建文件夹

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

当前板子为STM32F103VE

添加startup文件, startup_stm32f10x_hd.s

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

该文件夹创建mian.c

#include "stm32f10x.h"

int main (void)
{
}

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

编译,产生hex,axf等用于烧录的输出文件

使用特权

评论回复
27
喂什么玩意|  楼主 | 2024-3-31 21:03 | 只看该作者
寄存器点灯
#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)
{
        //函数体空,因为不引用外部文件,避免报错
}

使用特权

评论回复
28
喂什么玩意|  楼主 | 2024-3-31 21:04 | 只看该作者
GPIO功能框图
GPIO

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

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




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

使用特权

评论回复
29
喂什么玩意|  楼主 | 2024-3-31 21:04 | 只看该作者
推挽输出
​ 推挽输出

INT -> ODR -> 0/1

使用特权

评论回复
30
喂什么玩意|  楼主 | 2024-3-31 21:04 | 只看该作者
总结:什么叫推挽输出?
1、可以输出高低电平,用于连接数字器件,高电平由VDD决定,低电平由VSS决定。

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

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

使用特权

评论回复
31
喂什么玩意|  楼主 | 2024-3-31 21:05 | 只看该作者
开漏输出
​ 开漏输出

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

使用特权

评论回复
32
喂什么玩意|  楼主 | 2024-3-31 21:05 | 只看该作者
总结:什么叫开漏输出?
1、只能输出低电平,不能输出高电平。

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

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

GPIO输出初始化顺序

1、选定具体的GPIO

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

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

使用特权

评论回复
33
喂什么玩意|  楼主 | 2024-3-31 21:05 | 只看该作者
构建库函数雏形
寄存器点灯_类似固件库方法
//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)
       

使用特权

评论回复
34
喂什么玩意|  楼主 | 2024-3-31 21:05 | 只看该作者
#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)
{
        //函数体空,因为不引用外部文件,避免报错
}

使用特权

评论回复
35
喂什么玩意|  楼主 | 2024-3-31 21:06 | 只看该作者
寄存器点灯_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)

使用特权

评论回复
36
喂什么玩意|  楼主 | 2024-3-31 21:06 | 只看该作者
#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)
{
        //函数体空,因为不引用外部文件,避免报错
}

使用特权

评论回复
37
喂什么玩意|  楼主 | 2024-3-31 21:06 | 只看该作者
寄存器点灯_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*/

使用特权

评论回复
38
喂什么玩意|  楼主 | 2024-3-31 21:06 | 只看该作者
#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)
{
        //函数体空,因为不引用外部文件,避免报错
}

使用特权

评论回复
39
喂什么玩意|  楼主 | 2024-3-31 21:07 | 只看该作者
寄存器点灯_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*/

使用特权

评论回复
40
喂什么玩意|  楼主 | 2024-3-31 21:07 | 只看该作者
#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;
  }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则