三、【入门篇】GPIO、寄存器相关操作

入门篇-GPIO、寄存器相关操作

    • 1、芯片管脚以及功能
    • 2、GPIO 输入与输出
    • 3、寄存器的地址
    • 4、对寄存器进行按位操作
      • 寄存器操作小练习:
    • 5、驱动LED灯
    • 6、驱动蜂鸣器
    • 7、GPIO的输入功能

由表象到里象了解智能终端设备
通过画图的方式,了解芯片内部的组成部分
STM32 = CPU + 内存 + 硬盘 + 外设
了解ARM公司以及ST公司
通过最新选型手册,了解芯片的外设个数
内存20Kb 硬盘128Kb 64管脚封装 外设数量
通过讲硬件的手册,了解芯片内部的结构
ARM-CORTEX-M3 72MHz

1、芯片管脚以及功能

CPU是通过三总线来访问外设的:(数据总线 地址总线 控制总线)
AHB AHB1 AHB2都是三总线,叫做高速总线,APB1 APB2也是三总线,叫做外设总线。

芯片的管脚是多功能复用型的管脚:

  • 输入功能:上拉输入,下拉输入,浮空输入
  • 输出功能:推挽输出:管脚既可以输出高电平,也可以输出低电平
    开漏输出:管脚只能输出低电平,输出不了高电平
  • 复用功能:推挽复用,开漏复用
  • 模拟功能:模拟输入

2、GPIO 输入与输出

GPIO的输入与输出
输入功能相当于检测
输出功能相当于控制
LED灯->PC9管脚
输出低电平 灯亮
输出高电平 灯灭
我们使用操作寄存器的方法驱动GPIO

3、寄存器的地址

“ 举例:想要把100赋值到0x12345678这个地址中? ”

0x12345678 = 100;		//Error
&(0x12345678) = 100;	//Error
*(0x12345678) = 100;	//Error
*(int *)(0x12345678) = 100;//Bingo 芯片是32位的
*(char *)(0x12345678) = 100;//Bingo
*(unsigned int *)(0x12345678) = 100;//Bingo
*(volatile unsigned int *)(0x12345678) = 100;//Bingo
volatile :  //用来防止编译器优化
volatile int pm_led = 0;

在芯片的手册中并不会直接告诉寄存器的地址
寄存器的真实地址 = 基地址 + 偏移地址

打开<STM32RBT6\DataSheet\STM32>目录下
	<STM32F10x中文参考手册.pdf>	P75
GPIOC_CRH的真实地址 = GPIOC组的基地址 + 寄存器的偏移地址
		= 0x40011000 + 0x04
*(volatile unsigned int *)(0x40011000 + 0x04)|=(3<<4);

4、对寄存器进行按位操作

清0 用按位与 &
置1 用按位或 |
按位或上 0 ,保持不变(|)
按位与上 1 ,保持不变( &)
当操作多位寄存器,赋不同值时,一定是先清0,再赋值

例:想要把GPIOC_CRH寄存器的第5 4位配置为 1 0,怎么办?

*(volatile unsigned int *)(0x40011000+0x04)&=~(3<<4);
*(volatile unsigned int *)(0x40011000+0x04)|=(1<<5);

寄存器操作小练习:

1.查手册,想要把STM32F103RBT6芯片的PC4管脚配置为10MHz的开漏输出,如何编写代码

*(volatile unsigned int *)(0x40011000+0x00) &= ~(3<<16);
*(volatile unsigned int *)(0x40011000+0x00) |=(1<<16)
*(volatile unsigned int *)(0x40011000+0x00) &=~(3<<18)
*(volatile unsigned int *)(0x40011000+0x00) |=(1<<18)
    
*(volatile unsigned int *)(0x40011000+0x00) &=~(15<<16)
*(volatile unsigned int *)(0x40011000+0x00) |=(5<<16);

2.已知一寄存器的地址为0x12345678,想要把该寄存器的第9位读出,如何编写代码

*(volatile unsigned int *)(0x12345678) & (1 << 9) == 0 //判断 
int ret = 0;
ret = *(volatile unsigned int *)(0x12345678)>>9 & 0x01; 

5、驱动LED灯

!!!首先需要通过开发板原理图找到LED灯连接的芯片管脚

LED0 PC1
LED1 PC2
LED2 PC3
输出高电平 灯亮
输出低电平 灯灭

三、【入门篇】GPIO、寄存器相关操作_第1张图片

例:操作并且点亮LED

/*
LED0	PC1
LED1	PC2
LED2	PC3
输出高电平	灯亮
输出低电平	灯灭
*/
#define GPIOC_CRL *(volatile unsigned int*)(0x40011000 + 0x00)  //端口配置低寄存器
#define GPIOC_ODR *(volatile unsigned int*)(0x40011000 + 0x0C)  //端口输出数据寄存器
...........
//时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
//初始化 LED0 LED1 LED2 
GPIOC_CRL |= ((3 << 4) | (3 << 8) | (3 << 12));//把PC1 2 3管脚配置为50MHz的输出模式
GPIOC_CRL &= ~((3 << 6) | (3 << 10) | (3 << 14));//把PC1 2 3管脚配置为推挽的输出模式
GPIOC_ODR &= ~((1 << 1) | (1 << 2) | (1 << 3));//把PC1 2 3管脚输出低电平
..............
GPIOC_ODR  |= (1 << nu);    //亮灯 nu = LED0 LED1 LED2 
GPIOC_ODR  &=~ (1 << nu);	//灭灯

6、驱动蜂鸣器

1.通过开发板原理图找到蜂鸣器所连接的管脚
2.分析LED灯的连接方式
BEEP PC7
输出高电平 响
输出低电平 不响
3.把<02led>项目工程拷贝一份,重命名<03buzzer>
4.在<03buzzer/mylib>目录中新建buzzer.c和buzzer.h
5.在<03buzzer/project>目录中,打开项目工程
6.在Keil的左边侧边栏中,把buzzer.c文件添加到组中
7.双击打开buzzer.c,以及打开buzzer.h文件,进行编写

三、【入门篇】GPIO、寄存器相关操作_第2张图片

/*
BEEP	PC7
输出高电平	响
输出低电平	不响
*/

#define GPIOC_CRL *(volatile unsigned int *)(0x40011000+0x00)
#define GPIOC_ODR *(volatile unsigned int *)(0x40011000+0x0C)

void buzzer_init(void)
{
     
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	//通过APB2总线使能GPIOC组的时钟
	
	GPIOC_CRL |= (3 << 28);//把PC7管脚配置为50MHz的输出速率
	GPIOC_CRL &= ~(3 << 30);//把PC7管脚配置为推挽输出
	
	GPIOC_ODR &= ~(1 << 7);//把PC7管脚输出低电平
}

void buzzer_on(void)
{
     
	GPIOC_ODR |= (1 << 7);//把PC7管脚输出高电平
}

void buzzer_off(void)
{
     
	GPIOC_ODR &= ~(1 << 7);//把PC7管脚输出低电平
}

7、GPIO的输入功能

输入功能相当于检测:使用轮询的方式检测按键是否被按下,使用中断的方式检测按键是否被按下
查看开发板原理图:通过开发板原理图分析芯片管脚有何种变化

KEY0 PC9 上拉输入
KEY1 PC8 上拉输入
KEY2 PA0 下拉输入
KEY0 KEY1在按键按下时应当检测到低电平
在按键没有按下时应当检测到高电平
KEY2在按键按下时应当检测到高电平
在按键没有按下时应当检测到低电平

三、【入门篇】GPIO、寄存器相关操作_第3张图片

由于输入功能相当于是检测,所以需要把PC9 PC8 PA0管脚配置为输入模式
输入模式分为上拉输入 下拉输入 浮空输入
上拉输入:芯片管脚默认是高电平,低电平会触发检测
下拉输入:芯片管脚默认是低电平,高电平会触发检测
浮空输入:芯片管脚默认既不是也不是低会随着外界电平变化而变化

/*
KEY0	PC9	上拉输入
KEY1	PC8	上拉输入
KEY2	PA0	下拉输入
KEY0 KEY1在没有按下时应当检测到高电平,按下检测到低电平
KEY2在没有按下时应当检测到低电平,按下检测到高电平
*/
#define GPIOA_CRL *(volatile unsigned int *)(GPIOA_BASE+0x00)
#define GPIOA_IDR *(volatile unsigned int *)(GPIOA_BASE+0x08)
#define GPIOA_ODR *(volatile unsigned int *)(GPIOA_BASE+0x0C)

#define GPIOC_CRH *(volatile unsigned int *)(GPIOC_BASE+0x04)
#define GPIOC_IDR *(volatile unsigned int *)(GPIOC_BASE+0x08)
#define GPIOC_ODR *(volatile unsigned int *)(GPIOC_BASE+0x0C)

void button_init(void)//初始化功能按键(GPIO管脚)的函数
{
     
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOA, ENABLE);
	//通过APB2总线使能GPIOC组和GPIOA组的时钟
	
	GPIOC_CRH &= ~((15 << 0) | (15 << 4));//把PC9 PC8管脚配置为输入模式
	GPIOC_CRH |= ((2 << 2) | (2 << 6));//把PC9 PC8管脚配置为上拉/下拉的输入模式
	
	GPIOA_CRL &= ~(15 << 0);//把PA0管脚配置为输入模式
	GPIOA_CRL |= (2 << 2);//把PA0管脚配置为上拉/下拉的输入模式
	
	GPIOC_ODR |= ((1 << 9) | (1 << 8));//默认把PC9 PC8管脚拉高
	GPIOA_ODR &= ~(1 << 0);//默认把PA0管脚拉低
}

int button_status(int nu)//通过形参nu检测按键的电平高低,并且以返回值的形式返回
{
     
	int ret = 0;//通过ret变量接收管脚的电平高低
	
	switch(nu)
	{
     
		case 0 : ret = (GPIOC_IDR >> 9) & 0x01; break;
		case 1 : ret = (GPIOC_IDR >> 8) & 0x01; break;
		case 2 : ret = GPIOA_IDR & 0x01; ret = !ret; break;
	}//为了统一三个按键,返回1代表没有按下,返回0代表按下
	return !ret;//避免和ret变量的初始化值产生影响
}
//返回1代表按下 返回0代表没有按下

你可能感兴趣的:(单片机,stm32,嵌入式硬件)