由表象到里象了解智能终端设备
通过画图的方式,了解芯片内部的组成部分
STM32 = CPU + 内存 + 硬盘 + 外设
了解ARM公司以及ST公司
通过最新选型手册,了解芯片的外设个数
内存20Kb 硬盘128Kb 64管脚封装 外设数量
通过讲硬件的手册,了解芯片内部的结构
ARM-CORTEX-M3 72MHz
CPU是通过三总线来访问外设的:(数据总线 地址总线 控制总线)
AHB AHB1 AHB2都是三总线,叫做高速总线,APB1 APB2也是三总线,叫做外设总线。
芯片的管脚是多功能复用型的管脚:
GPIO的输入与输出
输入功能相当于检测
输出功能相当于控制
LED灯->PC9管脚
输出低电平 灯亮
输出高电平 灯灭
我们使用操作寄存器的方法驱动GPIO
“ 举例:想要把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);
清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;
!!!首先需要通过开发板原理图找到LED灯连接的芯片管脚
LED0 PC1
LED1 PC2
LED2 PC3
输出高电平 灯亮
输出低电平 灯灭
例:操作并且点亮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); //灭灯
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文件,进行编写
/*
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管脚输出低电平
}
输入功能相当于检测:使用轮询的方式检测按键是否被按下,使用中断的方式检测按键是否被按下
查看开发板原理图:通过开发板原理图分析芯片管脚有何种变化
KEY0 PC9 上拉输入
KEY1 PC8 上拉输入
KEY2 PA0 下拉输入
KEY0 KEY1在按键按下时应当检测到低电平
在按键没有按下时应当检测到高电平
KEY2在按键按下时应当检测到高电平
在按键没有按下时应当检测到低电平
由于输入功能相当于是检测,所以需要把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代表没有按下