题目要求
假设你手中已有 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)用C语言 寄存器方式编程实现。
一、GPIOx端口的各寄存器地址和详细参数
首先需要知道的是,STM32中对于GPIO口的操作,无非就是操作下面的寄存器而已,所谓的标准库也好,HAL库也好,它们都只是对操作寄存器的过程进行了封装,目的是为了减轻编程时的工作负担:
两个32位的配置寄存器:GPIOx_CRL、GPIOx_CRH
两个32位数据寄存器:GPIOx_IDR、GPIOx_ODR
一个32位的置位/复位寄存器:GPIOx_BSRR
一个16位复位寄存器:GPIOx_BRR
一个32位锁定寄存器:GPIOx_LCKR
- 时钟地址 在stm32手册中我们可以查到寄存器组起始地址,得到时钟地址如图下 - GPIO地址
可以知道时钟RCC属于AHB总线,然后GPIO端口A B C属于APB2总线。
- GPIO的配置寄存器CRL和CRH
每个 GPI/O 端口有两个 32 位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个 32 位数据寄存器(GPIOx_IDR,GPIOx_ODR),一个 32 位置位/复位寄存器 (GPIOx_BSRR),一个 16 位复位寄存器(GPIOx_BRR)和一个 32 位锁定寄存器 (GPIOx_LCKR)。 这里我们用的就是CRL和CRH,这两个寄存器的全称是:端口配置低寄存器(GPIOx_CRL) (x=A…E) 和 端口配置高寄存器(GPIOx_CRH) (x=A…E)。 根据数据手册中列出的每个 I/O 端口的特定硬件特征, GPIO 端口的每个位可以 由软件分别配置成多种模式。 ? 输入浮空 ? 输入上拉 ? 输入下拉 ? 模拟输入 ? 开漏输出 ? 推挽式输出 ? 推挽式复用功能 ? 开漏复用功能 每个 I/O 端口位可以自由编程,然而 I/0 端口寄存器必须按 32 位字被访问(不允许 半字或字节访问)。GPIOx_BSRR 和 GPIOx_BRR 寄存器允许对任何 GPIO 寄存 器的读/更改的独立访问;这样,在读和更改访问之间产生 IRQ 时不会发生危险。
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOB_CRL *((unsigned volatile int*)0x40010C00)
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
CRL CRH
GPIOA_CRL&=0xFFF0FFFF;
GPIOA_CRL|=0x00020000;
GPIOB_CRL&=0xFF0FFFFF;
GPIOB_CRL|=0x00200000;
GPIOC_CRH&=0xFF0FFFFF;
GPIOC_CRH|=0x00200000;
二、用C语言 寄存器方式编程实现
1.新建文件
打开keil5,在project下选择New uVision Project,输入文件名LEDLIGHT,然后选择与我们相符合的STM32F103C8
然后在左侧目录下点开Target 1,右键Source Group 1,选择Add New ITem to Group 。
然后选择C File(.c)文件
2.代码写入
2.1 启动代码
2.1.1 介绍启动代码
- 启动代码是一段和硬件相关的汇编代码
主要作用如下: 1、堆栈(SP)的初始化 2、初始化程序计数器(PC) 3、设置向量表异常事件的入口地址 4、调用 main 函数 - ST 公司提供了 3 个启动文件给我们,分别用于不同容量的 STM32 芯片,这三个文件是:
startup_stm32f10x_ld.s startup_stm32f10x_md.s startup_stm32f10x_hd.s - 其中,ld.s 适用于小容量 产品;md.s 适用于中等容量产品;hd 适用于大容量产品;这里的容量是指 FLASH 的大小
判断方法如下: 小容量:FLASH≤32K 中容量:64K≤FLASH≤128K 大容量:256K≤FLASH - 查阅数据手册,可知C8T6的Flash容量为128K,属于中容量,因此这里采用
startup_stm32f10x_md.s 作为启动文件
2.1.2 添加启动代码
- STM32F103C8T6核心板启动文件下载链接:
链接:https://pan.baidu.com/s/1Elgc4nvxXjiHLSZ2nXnSCQ 提取码:bmba 解压后找到md.s后缀的启动文件
将这个启动文件复制粘贴到文件夹下
在keil里,左侧Target 1目录下双击Source Group 1,发现找不到刚才添加的启动文件,需要将类型改成all,找到刚才添加的md.s文件,点击Add
- 点开小蓝棒图标,在output下勾选creat hex file,这里是后面要用。
2.2 写入代码
代码如下
#define RCC_AP2BENR *((unsigned volatile int*)0x40021018)
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ODR *((unsigned volatile int*)0x4001080C)
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ODR *((unsigned volatile int*)0x40010C0C)
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ODR *((unsigned volatile int*)0x4001100C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void A_LED_LIGHT(void);
void B_LED_LIGHT(void);
void C_LED_LIGHT(void);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
GPIOA_ODR=0x0<<5;
GPIOB_ODR=0x1<<9;
GPIOC_ODR=0x1<<14;
}
void B_LED_LIGHT(){
GPIOA_ODR=0x1<<5;
GPIOB_ODR=0x0<<9;
GPIOC_ODR=0x1<<14;
}
void C_LED_LIGHT(){
GPIOA_ODR=0x1<<5;
GPIOB_ODR=0x1<<9;
GPIOC_ODR=0x0<<14;
}
int main()
{
int j=100;
RCC_APB2ENR|=1<<2;
RCC_APB2ENR|=1<<3;
RCC_APB2ENR|=1<<4;
GPIOA_CRL&=0xFF0FFFFF;
GPIOA_CRL|=0X00200000;
GPIOA_ODR|=1<<5;
GPIOB_CRH&=0xFFFFFF0F;
GPIOB_CRH|=0x00000020;
GPIOB_ODR|=0x1<<9;
GPIOC_CRH&=0xF0FFFFFF;
GPIOC_CRH|=0x02000000;
GPIOC_ODR|=0x1<<14;
while(j)
{
A_LED_LIGHT();
Delay_ms(1000000);
B_LED_LIGHT();
Delay_ms(1000000);
C_LED_LIGHT();
Delay_ms(1000000);
}
}
void SystemInit(){
}
三、编译调试
1.编译
点击左上角的build图标,然后看到无报错警告,且已自动生成hex文件
2.调试
点击debug图标开始调试,发现出现以下错误
解决方案:我们点开小蓝棒图标,然后打开debug下,勾选Use Simulator.然后点击Utilities,取消勾选Use Debug Driver,点击setting,然后跳出来一个窗口,勾选reset and run。
总结
这篇文章简单介绍了用STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED,并搭建了电路,分别GPIOA-5、GPIOB-9、GPIOC-14 这3个引脚上控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。写出了包括GPIOx端口的各寄存器地址和详细参数和用C语言 寄存器方式编程实现。
|