这几天被隔离管着无聊,就上淘宝买了一块《正点原子》 STM32F103 nano开发板,来练习 stm32f103 的程式。买这块开发板的原因是因为,板子已经有大部分MPU可以应用的线路包括 LED, 七节显示,IR, BUZZ, botton, 和 ST-link 等等基本需要的线路。还附上一个小塑料盒,嶲带方便,也不需要接太多的线。毕竟只是练习了解 F072 和 F103 的差别。
另外,F103的资源多,找资料方便。
下边我就把过程记录一下。(这里舍去去正点原子网站取资料的过程)
开发板
从淘宝下单后,收到的东西是这样,原有的内付程式是个24 小时的时钟。
跑马灯
内附的跑马灯程式是使用左上方那一排红色 LED, 线路是固定好的,所以要看看线路是用高电平还是低电平控制亮点的。
从上面两张线路图可以看到两件事
- LED 的上头是 VCC输入,所以,下边的控制必须是低电平(“0”)的时候,LED会被点亮。
- LED 是被 PC0~7 控制的,也就是 GPIOC 这一組 I/O port.
程式解析
把开发板的资料从《正点原子》的网站下载下来后,找到“程序源码”找到“寄存器”版的标准程式。(建议把中文的档名改成英文,避免MDK 不认识而无法载入)
再找到实验一 的程式
再找到 里面的 USER 档案夹 ,点击 MDK 的 Project 档,就可以载入 跑马灯的全部程式。
直接编译程式与执行
main.c 档长得大概像这样子
里面包含了两个 main(), 其中一个是被注解掉的。
#include "led.h"
#include "delay.h"
#include "sys.h"
//****************************************
//****************************************
/*int main(void)
{
Stm32_Clock_Init(9); // 计时器是给 delay()用的
delay_init(72); //
LED_Init(); //按右键 用 goto xxxx 去看内容
/* 以下的 Main()是给只有两个onboard LED 开发板用的。程式很简单,就是两个 LED 交互一明一暗,每隔 500 ms 的亮灯。想要知道 LED0 是谁控制,就按右键 goto difinition of ‘LED0',了解。*/
while(1)
{
LED0=0;//LED0
LED1=1;//LED1
delay_ms(500);
LED0=1;//LED0
LED1=0;//LED1
delay_ms(500);
}
}
*/
int main(void)
{
u8 LED=0x00;
u8 i=0;
Stm32_Clock_Init(9); //
delay_init(72); //
LED_Init(); //
while(1)
{
for(i=0;i<8;i++)
{
LED++;
GPIOC->BRR = LED;
delay_ms(200);
LED<<=1;//
}
LED++;
GPIOC->BRR = LED;
delay_ms(200);
LED=0xff;//
GPIOC->BSRR = LED;
delay_ms(200);
}
}
第一个 main() 的 LEDx ,我们查询的结果是定义在 LED.h 这个档案里
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//
//
#define LED0 PCout(0) // PC0
#define LED1 PCout(1) // PC1
#define LED2 PCout(2) // PC2
#define LED3 PCout(3) // PC3
#define LED4 PCout(4) // PC4
#define LED5 PCout(5) // PC5
#define LED6 PCout(6) // PC6
#define LED7 PCout(7) // PC7
void LED_Init(void);//
#endif
PCout(1) 在 sys.h 档案里
BIT_ADDR(addr, bitnum) 也在 sys.h 档案里
这样就可以从 sys.h 档里看到许多 F103 的寄存器的address 定义 与使用了。
第二段 main()
程式如下:
int main(void)
{
u8 LED=0x00;
u8 i=0;
Stm32_Clock_Init(9); //
delay_init(72); //
LED_Init(); //
while(1)
{
for(i=0;i<8;i++)
{
LED++;
GPIOC->BRR = LED;
delay_ms(200);
LED<<=1;//
}
LED++;
GPIOC->BRR = LED;
delay_ms(200);
LED=0xff;//
GPIOC->BSRR = LED;
delay_ms(200);
}
}
我们只看 LED_Init(); 这个副程式,是在 LED.c 这个档案里,如下:
#include "led.h"
//
//
//LED IO3
void LED_Init(void)
{
RCC->APB2ENR|=1<<4; //
GPIOC->CRL&=0X00000000;
GPIOC->CRL|=0X33333333;//PC0~7
GPIOC->ODR|=(1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<7);//PC.0~7
}
这张图是 GPIOC 的定义, 及 GPIO_TypeDef 这两个是被定义在 stm32f10x.h 这个档案里。我把相关的参数都找出来放在一起,抄录在下面。
//stm32f10x.h
/*!< Peripheral memory map */
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
由于 GPIOC 被指向一个结构叫 GPIO_TypeDef,这个时候 GPIOC->ODR 这一类的符号就容易看懂了。
第二段 main()里面的这一段是主要点灯的程式。
for(i=0;i<8;i++)
{
LED++; //点 bit 0 用的
GPIOC->BRR = LED;
/* 查资料,BRR 是’1‘的时候会把 ODR 输出重置为低电平’0‘。BRR 是’0‘的时候会,不影响当下的 ODR 内容。*/
delay_ms(200);
LED<<=1;//向前移动一个灯,原来的灯在等一下用 LED++ 补上
}
而下面这一段程式是做收尾的工作:
LED++; //for 回圈出来后的收尾,与回圈里是一样的
GPIOC->BRR = LED; //for 回圈出来后的收尾,与回圈里是一样的
delay_ms(200); //for 回圈出来后的收尾,与回圈里是一样的
LED=0xff;//
GPIOC->BSRR = LED; // 把 GPIOC 设成高电位,关闭 LED
delay_ms(200);
所以整个跑马灯是从LED0 开始,逐个点亮之后,全部熄灭,让后再一个循环。
我这一篇文章主要是在分析 header 档的运用方法。因为,c 语言要容易移植到不同的 MPU/CPU, 比较耗时间的就是改写这些 header 档。当然,各家 芯片公司都会尽量准备好这些档案。