最近开始接触STM32F107VCT6这款ARM Cortex-M3核心的处理器了,实验采用的开发板是神舟IV。神舟IV光盘所带的资料挺完整,演示代码分两个版本,IAR的EWARM和KEIL的MDK版本。 环境所需要的工具什么的都有了,破解版。
首先先搭建好环境,我目前安装的是MDK 4.12 装好后打开源码工程,如下:
这里值得一提的是stm32f10x库封装的很好,很给力。
首先,因为我们将要操作的是LED灯,板载了5个,1个用于电源指示,可供IO口控制的只有4个,分别是LED1,LED2,LED3,LED4,电路图如下:
这些LED等连接到了主控的PD端口,了解了这些将有利于理解下面的代码部分。
系统结构如下:
STM32F107x内存映射如下:
GPIO 映射如下:
代码中用到的外设地址资料如下:
从上面的内存映射图可知,SDRAM_BASE是从0x20000000开始,PERIPH_BASE是 0x40000000开始。
跟上面的GPIO寄存器映射一致。
GPIOD 端口对应的内存地址为: 0x40000000 + 0x10000 + 0x1400 = 0x40011400。
所有的GPIO端口都处于APB2总线,所以我们的代码在操作LED灯之前先配置APB2。
以下是对代码的分析:
#include "stm32f10x.h" //这个头文件很重要,里面定义了一些宏,管脚,寄存器等。
#include <stdio.h>
/*
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
#define GPIO_Pin_3 ((uint16_t)0x0008)
#define GPIO_Pin_4 ((uint16_t)0x0010)
#define GPIO_Pin_5 ((uint16_t)0x0020)
#define GPIO_Pin_7 ((uint16_t)0x0080)
*/
#define RCC_GPIO_LED RCC_APB2Periph_GPIOD
#define GPIO_LED_PORT GPIOD
#define GPIO_LED1 GPIO_Pin_2
#define GPIO_LED2 GPIO_Pin_3
#define GPIO_LED3 GPIO_Pin_4
#define GPIO_LED4 GPIO_Pin_7
#define GPIO_LED_ALL GPIO_LED1 |GPIO_LED2 |GPIO_LED3 |GPIO_LED4
/*
操作一个GPIO口,需要配置管脚,速率,以及模式
从电路图上可以看出,点亮一个LED灯只需将管脚对应的IO口拉低,反之则关闭LED
IO端口以P[A,B,C,D,...,]命名,对应的IO口为PX[0~15],如: PD0 ~ PD15
*/
void LED_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOB, GPIOC and AFIO clock */
/* RCC_APB2ENR 的第五位是PD端口的使能位 0x200000, 而且是处在APB2总线上*/
RCC_APB2PeriphClockCmd(RCC_GPIO_LED | RCC_APB2Periph_AFIO , ENABLE); //RCC_APB2Periph_AFIO
/* LEDs pins configuration */
/* STM32的GPIO端口在作为输出时,配置端口的时钟速率分别为 2、10、50MHz*/
GPIO_InitStructure.GPIO_Pin = GPIO_LED_ALL; /* 将实验所用的管脚选中 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 配置时钟频率为50MHz ,至于为什么要这么大,暂时还不懂,呵呵*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /* 这个是配置输出模式为推挽输出,既能输出高电平,也能是低电平 */
GPIO_Init(GPIO_LED_PORT, &GPIO_InitStructure); /* 函数内部配置了对应IO口的输入输出模式和时钟速率 */
}
/*
#ifdef USE_FULL_ASSERT //如果定义了这个宏,出现断言错误,会调用assert_failed进行错误捕获。__FILE__ 会替换成当前文件,__LINE__ 是错误行
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
void assert_failed(uint8_t* file, uint32_t line);
#else //如果没有定义这个宏,就什么都不做。
#define assert_param(expr) ((void)0)
#endif
*/
/*
stm32f10x_gpio.c 代码中实现
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
//这个做法,我觉得挺好。在定义了USE_FULL_ASSERT时,能捕获错误。
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
//stm库将寄存器的操作封装成了C语言的结构操作,
//GPIOx_BRR (bit reset register) 这个寄存器,是位复位寄存器,要将I/O管脚清0复位,只需要将对应的位赋值为1即可。
//为了方便进行端口的位操作,STM32提供BRR BSRR之类的寄存器去操作ODR的对应位。也可以不这样去使用,直接操作 GPIOx_ODR 寄存器,
//只是因为这个寄存器需要以字为单位进行访问。类似于51的方式。
GPIOx->BRR = GPIO_Pin;
}
//这个函数就是直接对端口进行操作
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
{
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
GPIOx->ODR = PortVal;
}
//这个函数是将端口对应位置置位。
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Pin));
GPIOx->BSRR = GPIO_Pin; //置位/复位寄存器,高16位用于复位(清0),功能和BRR一样。低16位用于置位,对应的是PX0 ~ PX15
}
*/
void Led_Turn_on_all(void)
{
/* Turn On All LEDs */
GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED_ALL); //
}
void Led_Turn_on_1(void)
{
/* Turn On LED1 */
GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED1);
}
void Led_Turn_on_2(void)
{
/* Turn On LED2 */
GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED2 );
}
void Led_Turn_on_3(void)
{
/* Turn On LED3 */
GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED3);
}
void Led_Turn_on_4(void)
{
/* Turn On LED3 */
GPIO_ResetBits(GPIO_LED_PORT, GPIO_LED4);
}
void Led_Turn_off_all(void)
{
/* Turn Off All LEDs */
/* 将这些位拉高,就会关闭LED*/
GPIO_SetBits(GPIO_LED_PORT, GPIO_LED_ALL);
}
void Led_Turn_on(u8 led)
{
Led_Turn_off_all();
/* Turn Off Select LED */
switch(led)
{
case 0:
Led_Turn_on_1();
break;
case 1:
Led_Turn_on_2();
break;
case 2:
Led_Turn_on_3();
break;
case 3:
Led_Turn_on_4();
break;
default:
Led_Turn_on_all();
break;
}
}
//这个函数是随便搞点循环进行延时操作
static void Delay_ARMJISHU(__IO uint32_t nCount)
{
for (; nCount != 0; nCount--);
}
int main(void)
{
u8 KeyNum = 0;
LED_config();
Led_Turn_on_all();
Delay_ARMJISHU(6000000);
Led_Turn_off_all();
Delay_ARMJISHU(6000000);
KeyNum=0;
/* Main loop */
while (1)
{
//Led_Turn_on_Hex(KeyNum);
Led_Turn_off_all();
Led_Turn_on(KeyNum%4); //从0~3不断变化
KeyNum++;
Delay_ARMJISHU(1000000);
}
}
//
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
按下 F7 进行编译,然后Ctrl + F5调试运行程序。开发板必须处在打开状态。
总结:
1。操作GPIO端口,首先需要打开外设总线,这里是APB2总线。
2。设置输入输出模式,速率。
3。剩下的就是对GPIO进行输入输出的操作了。
4。这里的所有操作都依赖于stm32f10x库,对于具体实现只是对寄存器操作的封装,以及内存地址的封装。
5。这款主控对于IO的操作上与51有些区别,主要体现在端口位操作上,51没有类似于BSRR BRR可直接操作位的寄存器。
本人是新手,刚入手这板子,以上仅是个人学习的一些积累,错误在所难免。