一、寄存器
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。
二、GPIO
1、GPIO口
以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。
GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:
复位和时钟控制RCC的地址从0x4002 1000开始;
可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
看手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。
控制LED需要输出高电平或是低电平,所以需要配置为输出。
由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。
配置引脚PB8,使用的寄存器是GPIOB_CRH。下面我们来寻找这个寄存器的地址。
推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。
所以我们控制LED延时闪烁也很简单,就是控制ODR寄存器先输出1,LED灯亮,延时一段时间,控制ODR寄存器先输出0,LED灯灭,一直循环,就能实现流水灯的效果。
代码如下:
GPIOC_ODR &= ~(1<<13);//配置输出低电平0
GPIOC_ODR |= (1<<13);//配置输出高电平1
2、GPIO口的初始化:
①对于单个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
第二步:设置GPIOA参数:输出OR输入,工作模式,端口翻转速率
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8; //设定要操作的管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
第三步:调用GPIOA口初始化函数,进行初始化。
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA
第四步:调用GPIO-SetBits函数,进行相应为的置位。
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出高
②对于多个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA,GPIOE的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
第二步:设置GPIOA,GPIOE参数:输出OR输入,工作模式,端口翻转速率
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
▶把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 第四个口,PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高
再设置GPIOE
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // 第三个口,PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高
1.配置时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启 GPIOB 端口时钟
2.初始化结构体
// @file stm32f10x_gpio.h
typedef struct
{
uint16_t GPIO_Pin; /*!< 选择要配置的 GPIO 引脚 */
GPIOSpeed_TypeDef GPIO_Speed; /*!< 选择 GPIO 引脚的速率 */
GPIOMode_TypeDef GPIO_Mode; /*!< 选择 GPIO 引脚的工作模式 */
}GPIO_InitTypeDef;
3.配置输入输出模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //输出模式为通用推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4 ; //选定输出端口为GPIO_Pin_4
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz; //输出速度为2M
GPIO_Init(GPIOA,&GPIO_InitStruct);
三、点亮流水灯
1、生成hex文件
首先创建工程:
三、点亮流水灯
1、生成hex文件
首先创建工程:
在在魔法棒中设置选择生成hex文件:
编写代码:
加入延时函数:
//延时函数
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
代码如下:
main.c
/*GPIOA外设基地址*/
#define GPIOA_BASE 0x40010800
/*GPIOB外设基地址*/
#define GPIOB_BASE 0x40010C00
/*GPIOC外设基地址*/
#define GPIOC_BASE 0x40011000
/*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/
#define RCC_APB2ENR *((unsigned volatile int*)0x40021018)
/*GPIOA寄存器地址,强制转换为指针*/
#define GPIOA_CRH *((unsigned volatile int*)0x40010804)
#define GPIOA_ODR *((unsigned volatile int*)0x4001080C)
/*GPIOB寄存器地址,强制转换为指针*/
#define GPIOB_CRL *((unsigned volatile int*)0x40010C00)
#define GPIOB_ODR *((unsigned volatile int*)0x40010C0C)
/*GPIOC寄存器地址,强制转换为指针*/
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ODR *((unsigned volatile int*)0x4001100C)
/*延时函数*/
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
/*主函数*/
int main()
{
RCC_APB2ENR|=1<<2; /*APB2-GPIOA外设时钟使能*/
RCC_APB2ENR|=1<<3; /*APB2-GPIOB外设时钟使能*/
RCC_APB2ENR|=1<<4; /*APB2-GPIOC外设时钟使能*/
GPIOA_CRH&=0xFFF0FFFF; /*设置位 清零*/
GPIOA_CRH|=0x00020000; /*PA12推挽输出*/
GPIOA_ODR&=1<<12; /*设置初始灯为亮*/
GPIOB_CRL&=0xFFFFFF0F; /*设置位 清零*/
GPIOB_CRL|=0x00000020; /*PB1推挽输出*/
GPIOB_ODR|=0<<1; /*设置初始灯为灭*/
GPIOC_CRH&=0xF0FFFFFF; /*设置位 清零*/
GPIOC_CRH|=0x02000000; /*PC14推挽输出*/
GPIOC_ODR|=0<<14; /*设置初始灯为灭*/
while(1)
{
GPIOA_ODR&= ~(1<<12); /*PA12高电平*/
Delay_ms(3000000);
GPIOA_ODR|= (1<<12); /*PA12低电平*/
Delay_ms(3000000);
GPIOB_ODR&= ~(1<<1); /*PB1高电平*/
Delay_ms(3000000);
GPIOB_ODR|= (1<<1); /*PB1低电平*/
Delay_ms(3000000);
GPIOC_ODR&= ~(1<<14); /*PC14高电平*/
Delay_ms(3000000);
GPIOC_ODR|= (1<<14); /*PC14低电平*/
Delay_ms(3000000);
}
}
CH341SeSetep也安装上
打开界面如图所示:
如果 USB 转串口驱动安装成功,USB 线跟板子连接没有问题,在计算机->管理->设备管理器->端口中可识别到串口。
打开C8T6数据手册,查找TXD和RXD管脚位置
PA9——TX
PA10——RX
- PA9——RXD
PA10——TXD
3.3V接电
GND接地
- 在FlyMcu界面勾选编程后执行,选择好读取的hex文件路径,在最下角选择RTS低电平复位,DTR高电平进BootLoader
设置bps和烧入的hex文件之后点击开始编程,上图所示就是程序烧入成功的
3、结果
四、总结
这次的实验内容是用寄存器的GPIOA、GPIOB、GPIOC口来点亮流水灯,在这个过程中,遇到了很多的问题,首先是关于寄存器的初始化,必须要查阅stm32f103c8芯片的手册,还在其他的博客上查阅关于面包板的使用,还有就是关于实物的连接都遇到了困难,不过好在网上的资料很多,在翻阅资料的过程中,学到了很多的东西,还有对于嵌入式的了解更加深入了。
一、寄存器
寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。
二、GPIO
1、GPIO口
以看出AHB总线包含RCC时钟控制,GPIO是属于APB2的。
GPIO端口B的地址从0x4001 0C00开始。接下来只寻找时钟使能寄存器的地址:
复位和时钟控制RCC的地址从0x4002 1000开始;
可以在6.3.7小节找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。
看手册RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。
控制LED需要输出高电平或是低电平,所以需要配置为输出。
由于STM32的每个IO都需要4个位来配置,所以一个32位的寄存器最大只能配置8个IO(32位的单片机的寄存器就是32位的)。STM32中,用端口配置低寄存器(GPIOx_CRL)来配置引脚Px0-Px7, 用端口配置高寄存器(GPIOx_CRH)来配置引脚Px8-Px15。
配置引脚PB8,使用的寄存器是GPIOB_CRH。下面我们来寻找这个寄存器的地址。
推挽输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。
开漏是需要外接上拉电阻才可以输出高电平的,这里并不适合。所以需要设置为推挽输出。
所以我们控制LED延时闪烁也很简单,就是控制ODR寄存器先输出1,LED灯亮,延时一段时间,控制ODR寄存器先输出0,LED灯灭,一直循环,就能实现流水灯的效果。
代码如下:
GPIOC_ODR &= ~(1<<13);//配置输出低电平0
GPIOC_ODR |= (1<<13);//配置输出高电平1
2、GPIO口的初始化:
①对于单个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
第二步:设置GPIOA参数:输出OR输入,工作模式,端口翻转速率
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8; //设定要操作的管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
第三步:调用GPIOA口初始化函数,进行初始化。
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA
第四步:调用GPIO-SetBits函数,进行相应为的置位。
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出高
②对于多个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA,GPIOE的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
第二步:设置GPIOA,GPIOE参数:输出OR输入,工作模式,端口翻转速率
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
▶把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 第四个口,PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高
再设置GPIOE
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // 第三个口,PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高
1.配置时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启 GPIOB 端口时钟
2.初始化结构体
// @file stm32f10x_gpio.h
typedef struct
{
uint16_t GPIO_Pin; /*!< 选择要配置的 GPIO 引脚 */
GPIOSpeed_TypeDef GPIO_Speed; /*!< 选择 GPIO 引脚的速率 */
GPIOMode_TypeDef GPIO_Mode; /*!< 选择 GPIO 引脚的工作模式 */
}GPIO_InitTypeDef;
3.配置输入输出模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; //输出模式为通用推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4 ; //选定输出端口为GPIO_Pin_4
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz; //输出速度为2M
GPIO_Init(GPIOA,&GPIO_InitStruct);
三、点亮流水灯
1、生成hex文件
首先创建工程:
三、点亮流水灯
1、生成hex文件
首先创建工程:
在在魔法棒中设置选择生成hex文件:
编写代码:
加入延时函数:
//延时函数
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
代码如下:
main.c
/*GPIOA外设基地址*/
#define GPIOA_BASE 0x40010800
/*GPIOB外设基地址*/
#define GPIOB_BASE 0x40010C00
/*GPIOC外设基地址*/
#define GPIOC_BASE 0x40011000
/*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/
#define RCC_APB2ENR *((unsigned volatile int*)0x40021018)
/*GPIOA寄存器地址,强制转换为指针*/
#define GPIOA_CRH *((unsigned volatile int*)0x40010804)
#define GPIOA_ODR *((unsigned volatile int*)0x4001080C)
/*GPIOB寄存器地址,强制转换为指针*/
#define GPIOB_CRL *((unsigned volatile int*)0x40010C00)
#define GPIOB_ODR *((unsigned volatile int*)0x40010C0C)
/*GPIOC寄存器地址,强制转换为指针*/
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ODR *((unsigned volatile int*)0x4001100C)
/*延时函数*/
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
/*主函数*/
int main()
{
RCC_APB2ENR|=1<<2; /*APB2-GPIOA外设时钟使能*/
RCC_APB2ENR|=1<<3; /*APB2-GPIOB外设时钟使能*/
RCC_APB2ENR|=1<<4; /*APB2-GPIOC外设时钟使能*/
GPIOA_CRH&=0xFFF0FFFF; /*设置位 清零*/
GPIOA_CRH|=0x00020000; /*PA12推挽输出*/
GPIOA_ODR&=1<<12; /*设置初始灯为亮*/
GPIOB_CRL&=0xFFFFFF0F; /*设置位 清零*/
GPIOB_CRL|=0x00000020; /*PB1推挽输出*/
GPIOB_ODR|=0<<1; /*设置初始灯为灭*/
GPIOC_CRH&=0xF0FFFFFF; /*设置位 清零*/
GPIOC_CRH|=0x02000000; /*PC14推挽输出*/
GPIOC_ODR|=0<<14; /*设置初始灯为灭*/
while(1)
{
GPIOA_ODR&= ~(1<<12); /*PA12高电平*/
Delay_ms(3000000);
GPIOA_ODR|= (1<<12); /*PA12低电平*/
Delay_ms(3000000);
GPIOB_ODR&= ~(1<<1); /*PB1高电平*/
Delay_ms(3000000);
GPIOB_ODR|= (1<<1); /*PB1低电平*/
Delay_ms(3000000);
GPIOC_ODR&= ~(1<<14); /*PC14高电平*/
Delay_ms(3000000);
GPIOC_ODR|= (1<<14); /*PC14低电平*/
Delay_ms(3000000);
}
}
CH341SeSetep也安装上
打开界面如图所示:
如果 USB 转串口驱动安装成功,USB 线跟板子连接没有问题,在计算机->管理->设备管理器->端口中可识别到串口。
打开C8T6数据手册,查找TXD和RXD管脚位置
PA9——TX
PA10——RX
- PA9——RXD
PA10——TXD
3.3V接电
GND接地
- 在FlyMcu界面勾选编程后执行,选择好读取的hex文件路径,在最下角选择RTS低电平复位,DTR高电平进BootLoader
设置bps和烧入的hex文件之后点击开始编程,上图所示就是程序烧入成功的
3、结果
四、总结
这次的实验内容是用寄存器的GPIOA、GPIOB、GPIOC口来点亮流水灯,在这个过程中,遇到了很多的问题,首先是关于寄存器的初始化,必须要查阅stm32f103c8芯片的手册,还在其他的博客上查阅关于面包板的使用,还有就是关于实物的连接都遇到了困难,不过好在网上的资料很多,在翻阅资料的过程中,学到了很多的东西,还有对于嵌入式的了解更加深入了。
举报