找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4585|回复: 0
收起左侧

学习笔记-STM32 建立工程、流水灯、宏##链接符

[复制链接]
ID:91350 发表于 2015-9-30 01:01 | 显示全部楼层 |阅读模式
这段时间加班加的厉害,今天很努力的睡觉。。。
复习这些之后,感觉怪怪的,不学吧,又觉得自己没有系统的去学,学吧,又觉得重复着学旧知识有些浪费时间,蛮矛盾的心理。
抱着“学而时习之,不亦说乎”的心态继续观看视频和实验,竟然有新的发现,哈哈,正所谓 “温故而知新,可以为师矣”,古人诚不欺我也。
最后花了一些时间研究宏的 ## 连接符。 有收获。。。

---------------------------------------------------------------------------------
软件:MDK V5.12  固件库 3.5

1、新建文件夹名为 MyProject

2、在该文件夹内分别新建 USER、CORE、OBJ、STM3210x_FWLib

3、CORE 存放核心文件和启动文件(不同容量需要不同的启动文件)
        将 STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport
        中 core_cm3.h 、core_cm3.c 文件拷贝到此目录
        将 startup_stm32f10x_hd.s(大容量启动文件) 文件拷贝到此目录
       
4、OBJ 存放编译过程文件以及 hex 文件

5、STM32F10x_FWLib 存放 STM32 库函数文件
        将 STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver
        中 inc、src 文件夹拷贝到此目录
       
6、USER 用来存放工程文件、主函数文件 main.c、以及其他包括 system_stm32f10x.c 等
        将 STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
        中 stm32f10x.h、system_stm32f10x.h、system_stm32f10x.c 拷贝到此目录
        将 STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template
        中 stm32f10x_it.h、stm32f10x_it.c、stm32f10x_conf.h 拷贝到此目录
        在此目录中新建 main.c 文件
       
7、启动 MDK -> Project -> 选中 MyProject\USER 文件夹 -> 工程名为 MyProject

8、弹出 Select Device for Target 'Target 1' 对话框,选择 STMicroelectronics
        -> STM32F1 Series -> STM32F103 -> STM32F103ZE -> OK

9、右击 Target 1 -> Mange Project Items -> Project Targets -> Target 1 修改为 MyProject
        -> Groups 里新建 USER、CORE、OBJ、STM3210x_FWLib 分组。
       
10、为每个分组添加相对应目录里面的.c文件,注意 CORE 需要将 startup_stm32f10x_hd.s 加入。

11、点击魔术棒工具,Output -> Select Folder for Objects -> 选择为 OBJ 目录,勾选 Create HEX File

12、魔术棒工具 -> C/C++ -> Preprocessor Symbols -> Define 写入 STM32F10X_HD,USE_STDPERIPH_DRIVER
        因为 3.5 版本的库函数在配置和选择外设的时候是通过宏定义来选择的,我们需要配置一个全局的宏定义
        变量。

13、魔术棒工具 -> C/C++ -> Include Paths 加入头文件的目录
        MDK,只会从这里定义的目录去找头文件,注意,它只会搜索一级目录。
       
--------------------------------------------------------------------------------------------------------

基本的IO口操作;

一、初始化函数:
        void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct);
        功能:
                设置IO口为输入或输出模式、翻转速度
        参数:
                GPIOx:IO组 GPIOA ~ GPIOG
                GPIO_InitStruct:
                        .GPIO_Pin:IO引脚、
                        .GPIO_Speed:翻转速度 GPIO_Speed_10MHz、GPIO_Speed_2MHz、GPIO_Speed_50MHz
                        .GPIO_Mode:模拟输入、浮空输入、上下拉输入、推挽输出、开漏输出、复用推挽、复用开漏
        返回值:
               
       
二、读取函数
        1、读取输入电平函数(IO口被设置为输入模式)
        uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);               
        功能:
                当IO口被设置为输入模式时,读取指定组指定IO电平状态 读取 IDR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:IO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
                0:为低电平
                1:为高电平
               
        uint16_t GPIO_ReadInputData(GPIO_TypeDef *GPIOx);
        功能:
                当IO口被设置为输入模式时,读取指定GPIO组的整组IO电平状态 读取 IDR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:IO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
                整组IO口的所有状态集合为一个 16 位的整数,该整数每一位对应一个IO口的状态

        2、读取输出电平函数(IO口被设置为输出模式)
        uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
        功能:
                当IO口被设置为输出模式时,读取指定GPIO组指定IO电平状态 读取 ODR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:GPIO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
                0:为低电平
                1:为高电平
                uint16_t GPIO_ReadOutputData(GPIO_TypeDef *GPIOx);
        功能:
                当IO口被设置为输出模式时,读取指定GPIO组指定IO电平状态 读取 ODR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:GPIO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
                整组IO口的所有状态集合为一个 16 位的整数,该整数每一位对应一个IO口的状态
               
       
三、设置函数
        1、设指定IO口输出高电平还是低电平
        void GPIO_SetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
        功能:
                设置指定GPIO组指定IO口为高电平 操作 BSRR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:GPIO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
               
               
        void GPIO_ResetBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
        功能:
                设置指定GPIO组指定IO口为低电平 操作 BRR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:IO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
        返回值:
               
               
        2、整体操作
        void GPIO_WriteBit(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
        功能:
                GPIO_SetBits()、GPIO_ResetBits() 的结合体,根据 BitVal 操作 BSRR 或 BRR 寄存器
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                GPIO_Pin:GPIO组中某个引脚 GPIO_Pin_0 ~ GPIO_Pin_15
                BitVal:Bit_RESET 设置为低电平   Bit_SET 设置为高电平
        返回值:
               
               
        void GPIO_Write(GPIO_TypeDef *GPIOx, uint16_t PortVal);
        功能:
                设置指定GPIO组整组IO电平状态 操作 ODR 寄存器,在设置输出模式时 ODR 寄存器决定输入上拉还是下拉
        参数:
                GPIOx:GPIO组 GPIOA ~ GPIOG
                PortVal:16 位整数,每一位对应一个IO口的状态
        返回值:
                               


四、GPIO 时钟
        void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
        功能:
                设置挂载在APB2总线上的设备时钟,GPIOA ~ GPIOG 就是挂载到 APB2 中
        参数:
                RCC_APB2Periph:选择外设
                NewState:ENABLE 使能、DISABLE 关闭
        返回值:
               

五、遇到的问题
        1、在编写流水灯的时候,我是先初始化GPIO,再开启IO时钟。发现无法点亮LED灯。
                注意,必须先开启时钟,再初始化IO
               
        2、想利用宏连接 ## 符号来提高代码可移植性, 这里花的时间比较长。
                有如下宏定义:
                #define LED1_GPIO       GPIOB
                #define __ToRCC(GPIO)   RCC_APB2Periph_##GPIO
                #define ToRCC(GPIO)   __ToRCC(GPIO)
               
                使用方式:
                ToRCC(LED1_GPIO)
                原以为会扩展为:RCC_APB2Periph_GPIOB 但实际上是 RCC_APB2Periph_
                只知道是因为 GPIOB 自身也是一个宏导致这种现象,至于为什么过程中会丢失就不清楚了。
               
                目前的解决方式是再套一个宏,感觉这种方式比较麻烦,不知道还有没有更好的方法。
                #define LED1_GPIO       B
                #define __GPIOToRCC(GPIO)   RCC_APB2Periph_GPIO##GPIO
                #define GPIOToRCC(GPIO)   __GPIOToRCC(GPIO)
               
                使用方式:
                GPIOToRCC(LED1_GPIO) 此时可以正确扩展为 RCC_APB2Periph_GPIOB
               

六、源文件
---------------------------------------------------------------------------
public.h
---------------------------------------------------------------------------

#include "stm32f10x.h"


// 比较有意思的宏定义 ------------------------------------------------------------


#define __TORCC(group)      RCC_APB2Periph_GPIO##group
#define TORCC(group)        __TORCC(group)


#define __TOGPIO(group)     GPIO##group
#define TOGPIO(group)       __TOGPIO(group)


#define __TOPIN(pin)        GPIO_Pin_##pin
#define TOPIN(pin)          __TOPIN(pin)


// 比较有意思的宏定义 ------------------------------------------------------------

---------------------------------------------------------------------------
led.h
---------------------------------------------------------------------------
#ifndef __LED_H
#define __LED_H

enum en_LED_NUMBER
{
    LED_NUMBER_1,
    LED_NUMBER_2,
    LED_NUMBER_3
};

void LED_Init(void);
char LED_On(enum en_LED_NUMBER number);
char LED_Off(enum en_LED_NUMBER number);
char LED_On_Mutex(enum en_LED_NUMBER number);
char LED_Off_Mutex(enum en_LED_NUMBER number);
void LED_On_ALL(void);
void LED_Off_ALL(void);

#endif

---------------------------------------------------------------------------
led.h
---------------------------------------------------------------------------
#include "public.h"

#include "led.h"

// 移植部分 ------------------------------------------------------------

#define LED1_GPIO       B
#define LED1_GPIO_Pin   0

#define LED2_GPIO       F
#define LED2_GPIO_Pin   7

#define LED3_GPIO       F
#define LED3_GPIO_Pin   8

#define LEDON(GPIOx, GPIO_Pin)  GPIO_ResetBits(GPIOx, GPIO_Pin)
#define LEDOFF(GPIOx, GPIO_Pin)  GPIO_SetBits(GPIOx, GPIO_Pin)

// 移植部分 ------------------------------------------------------------

/* 点亮指定 LED 灯 */
char LED_On(enum en_LED_NUMBER number)
{
    switch((int)number)
    {
        case LED_NUMBER_1:
            LEDON(TOGPIO(LED1_GPIO), TOPIN(LED1_GPIO_Pin));
            break;
        case LED_NUMBER_2:
            LEDON(TOGPIO(LED2_GPIO), TOPIN(LED2_GPIO_Pin));
            break;
        case LED_NUMBER_3:
            LEDON(TOGPIO(LED3_GPIO), TOPIN(LED3_GPIO_Pin));
            break;
        default:
            return 1;
    }
   
    return 0;
}

/* 熄灭指定 LED 灯 */
char LED_Off(enum en_LED_NUMBER number)
{
    switch((int)number)
    {
        case LED_NUMBER_1:
            LEDOFF(TOGPIO(LED1_GPIO), TOPIN(LED1_GPIO_Pin));
            break;
        case LED_NUMBER_2:
            LEDOFF(TOGPIO(LED2_GPIO), TOPIN(LED2_GPIO_Pin));
            break;
        case LED_NUMBER_3:
            LEDOFF(TOGPIO(LED3_GPIO), TOPIN(LED3_GPIO_Pin));
            break;
        default:
            return 1;
    }
   
    return 0;
}

/* 只允许其中一个 LED 亮 ,其余皆灭 */
char LED_On_Mutex(enum en_LED_NUMBER number)
{
    switch((int)number)
    {
        case LED_NUMBER_1:
            LED_On(LED_NUMBER_1);
            LED_Off(LED_NUMBER_2);
            LED_Off(LED_NUMBER_3);
            break;
        
        case LED_NUMBER_2:
            LED_Off(LED_NUMBER_1);
            LED_On(LED_NUMBER_2);
            LED_Off(LED_NUMBER_3);
            break;
        
        case LED_NUMBER_3:
            LED_Off(LED_NUMBER_1);
            LED_Off(LED_NUMBER_2);
            LED_On(LED_NUMBER_3);
            break;
        
        default:
            return 1;
    }
   
    return 0;
}

/* 只允许其中一个 LED 灭 ,其余皆亮 */
char LED_Off_Mutex(enum en_LED_NUMBER number)
{
    switch((int)number)
    {
        case LED_NUMBER_1:
            LED_Off(LED_NUMBER_1);
            LED_On(LED_NUMBER_2);
            LED_On(LED_NUMBER_3);
            break;
        
        case LED_NUMBER_2:
            LED_On(LED_NUMBER_1);
            LED_Off(LED_NUMBER_2);
            LED_On(LED_NUMBER_3);
            break;
        
        case LED_NUMBER_3:
            LED_On(LED_NUMBER_1);
            LED_On(LED_NUMBER_2);
            LED_Off(LED_NUMBER_3);
            break;
        
        default:
            return 1;
    }
   
    return 0;
}

void LED_On_ALL(void)
{
    LED_On(LED_NUMBER_1);
    LED_On(LED_NUMBER_2);
    LED_On(LED_NUMBER_3);
    return ;
}

void LED_Off_ALL(void)
{
    LED_Off(LED_NUMBER_1);
    LED_Off(LED_NUMBER_2);
    LED_Off(LED_NUMBER_3);
    return ;
}

/* 初始化 LED */
void LED_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
   
    // 初始化IO时钟
    RCC_APB2PeriphClockCmd(TORCC(LED1_GPIO), ENABLE);
    RCC_APB2PeriphClockCmd(TORCC(LED2_GPIO), ENABLE);
    RCC_APB2PeriphClockCmd(TORCC(LED3_GPIO), ENABLE);
   
    // 初始化 GPIO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   
    GPIO_InitStructure.GPIO_Pin = TOPIN(LED1_GPIO_Pin);   
    GPIO_Init(TOGPIO(LED1_GPIO), &GPIO_InitStructure);
   
    GPIO_InitStructure.GPIO_Pin = TOPIN(LED2_GPIO_Pin);
    GPIO_Init(TOGPIO(LED2_GPIO), &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = TOPIN(LED3_GPIO_Pin);
    GPIO_Init(TOGPIO(LED3_GPIO), &GPIO_InitStructure);
       
    LED_Off_ALL();
}



---------------------------------------------------------------------------
main.c
---------------------------------------------------------------------------
#include "public.h"

#include "delay.h"
#include "led.h"


int main(void)
{
    delay_init();

    LED_Init();

    while(1)
    {
        delay_ms(500);
        LED_On_Mutex(LED_NUMBER_1);
        delay_ms(500);
        LED_On_Mutex(LED_NUMBER_2);
        delay_ms(500);
        LED_On_Mutex(LED_NUMBER_3);
    }

}
---------------------------------------------------------------------------


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表