Keil开发STM32单片机项目的三种方式

    STM32单片机相比51单片机,内部结构复杂很多,因此直接对底层寄存器编码,相对复杂,这个需要我们了解芯片手册,对于复杂项目,这些操作可能需要反复编写,因此出现了标准库的方式,对寄存器操作进行了封装,操作相对简单。随着项目复杂度提升,又出现了封装更厉害的一种库HAL,这个需要借助STM32CubeMx工具来生成代码。

    简单来说,STM32编码有三种方式:

    1、寄存器编码。

    2、标准库操作编码。

    3、HAL库操作编码。

    这三种方式各有优劣势,寄存器编码更偏向底层,方便我们熟悉单片机内部结构,开发效率相对较低,因为很多初始化,赋值操作需要反复编写,使用HAL库,可以减少大量代码,提高了开发效率,但是不便于理解底层逻辑,需要非常熟练STM32单片机工作逻辑之后才好上手。

    第一种寄存器编码方式,构建项目可以直接新建一个普通项目,如下所示:

 

    项目新建之后,什么也没有,我们需要手动添加一个启动文件和一个main.c文件到工程目录:

 

    我们在keil工具里面,通过添加现有项的方式将这两个文件加到Source Group 1 中,如下所示:

     这个时候,编译会报错:

    说是SystemInit符号未定义,其实是我们构建的项目代码里面(main.c)中缺少SystemInit()方法,添加上就可以了。

    这个项目只有两个文件,一个是启动文件,一个是主程序main.c。我们操作寄存器的代码就在main.c中编写,这里给出一个简单的实现LED灯闪烁的示例代码:

#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE+ 0x10000)
#define GPIOA_BASE (APB2PERIPH_BASE+0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE+0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE+0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE+0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE+0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE+0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE+0x2000)#define GPIOA_ODR_Addr (GPIOA_BASE+12)
#define GPIOB_ODR_Addr (GPIOB_BASE+12)
#define GPIOC_ODR_Addr (GPIOC_BASE+12)
#define GPIOD_ODR_Addr (GPIOD_BASE+12)
#define GPIOE_ODR_Addr (GPIOE_BASE+12)
#define GPIOF_ODR_Addr (GPIOF_BASE+12)
#define GPIOG_ODR_Addr (GPIOG_BASE+12)#define BITBAND(addr,bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr&0xFFFFF)<<5) + (bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define LED0 MEM_ADDR(BITBAND(GPIOA_ODR_Addr, 5))typedef struct
{volatile unsigned int CR;volatile unsigned int CFGR;volatile unsigned int CIR;volatile unsigned int APB2RSTR;volatile unsigned int APB1RSTR;volatile unsigned int AHBENR;volatile unsigned int APB2ENR;volatile unsigned int APB1ENR;volatile unsigned int BDCR;volatile unsigned int CSR;
}RCC_TypeDef;#define RCC ((RCC_TypeDef*)0x40021000)typedef struct
{volatile unsigned int CRL;volatile unsigned int CRH;volatile unsigned int IDR;volatile unsigned int ODR;volatile unsigned int BSRR;volatile unsigned int BRR;volatile unsigned int LCKR;
}GPIO_TypeDef;#define GPIOA ((GPIO_TypeDef*)GPIOA_BASE)void LEDInit(void)
{RCC->APB2ENR|=1<<2;GPIOA->CRL &= 0xFF0FFFFF;GPIOA->CRL |= 0x00300000;
}void Delay_ms(volatile unsigned int t)
{unsigned int i,j;for(j=0;j<t;j++)for(i=0;i<800;i++);
}void SystemInit(void)
{}int main(void)
{LEDInit();while(1){LED0 = 0;Delay_ms(500);LED0 = 1;Delay_ms(500);}
}

    项目构建成功,不报错就可以进行仿真,或者下载到单片机调试。

    第二种方式,直接在Keil工具中构建STM32标准库工程,不用额外拷贝标准库文件到项目文件夹,然后添加现有项的方式加入组中,构建项目,选择芯片系列之后,在弹出确认框这里可以选择需要的库:

    这里勾选CMSIS->CORE, Device->Startup , Device->StdPeriph Drivers->Framework,GPIO,RCC几项。

    自动生成的代码结构如下所示:

    这里面除了main.c是手动添加的,其余的都是通过keil自动生成的,如果你做过手动添加标准库,那么就会很熟悉这里面的一些文件,stm32f10x_gpio.c,stm32f10x_rcc.c,startup_stm32f10x_ld.s,system_stm32f10x.c。

    这里直接编译会报错:

    Error: L6218E: Undefined symbol assert_param (referred from misc.o).

    这个问题是找不到assert_param这个函数,而这个函数是在stm32f10x_conf.h中,从上面的工程结构,我们看到代码里面是有stm32f10x_conf.h这个头文件的。解决办法就是使用宏定义USE_STDPERIPH_DRIVER查找:

    点击魔法棒工具Options for Targets,在弹出框中选择c/c++,Define处输入USE_STDPERIPH_DRIVER,Include Paths指定当前项目路径下的RTE目录即可。

    最后可以编译成功。

    这里也给出一段让LED闪烁的代码。

#include "stm32f10x.h"void Delay()
{unsigned int i,j;for(i=0;i<1000;i++)for(j=0;j<1000;j++);
}void LED_Config()
{GPIO_InitTypeDef GPIO_InitStructure;                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;             GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     GPIO_Init(GPIOA, &GPIO_InitStructure);
}int main(void)
{LED_Config();while(1){GPIO_SetBits(GPIOA, GPIO_Pin_5);Delay();GPIO_ResetBits(GPIOA, GPIO_Pin_5);Delay();}
}

    PA5口作为电平输出, 时钟使能与GPIO初始化,都调用标准库中的方法。

    第三种构建STM32工程的方法需要借助STM32CubeMx工具,这个是免费的,安装之后,可以图形化界面操作,如下所示:

    1)打开工具之后,新建工程,来到选择芯片界面:

    这里在Commecial Part Number这里输入STM32F103会自动补全C6A,选中右侧面板中的一个芯片双击,

   2)进入配置界面。

    展开System Core菜单,默认选中SYS,我们点击,在中间模式这里选择Debug: Serial Wire。

    然后,选中RCC

    设置HSE/LSE为Crystal/Ceramic Resonator。改变之后,右侧芯片会有变化。

    接着,还要将芯片上的PA5端口作为GPIO_Output,点击PA5,就会出现菜单选项,直接选择即可。 

   3)工程位置及编译工具设置

    设置工程名,工程位置,Toolchain/IDE选择MDK-ARM,版本选择V5。

    4)代码生成。点击GENERATE CODE按钮。

    5)生成的代码,可以直接用Keil打开,结构如下:

 

    连main.c都写好了,直接编译也没有问题。我们要实现LED闪烁,只需要在while(1)循环体中增加如下代码:

  while (1){/* USER CODE END WHILE */HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);HAL_Delay(200);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);HAL_Delay(200);/* USER CODE BEGIN 3 */}

    这种方式生成的代码,不仅帮我们做了很多工作,甚至连编译准备都做好了,我们看看Options for Targets的c/c++选项:

 

   宏定义查找设置了,不用我们手动设置,另外编译生成hex文件,也同样帮我们勾选好了。

   使用HAL库,自己编写的代码很少,但是理解起来需要花时间,默认操作做了什么,这里面隐含了哪些操作。   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/69153.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【解决】Kafka Exception thrown when sending a message with key=‘null‘ 异常

问题原因&#xff1a; 如下图&#xff0c;kafka 中配置的是监听域名的方式&#xff0c;但程序里使用的是 ip:port 的连接方式。 解决办法&#xff1a; kafka 中配置的是域名的方式&#xff0c;程序里也相应配置成 域名:port 的方式&#xff08;注意&#xff1a;本地h…

安卓13解决链接问题

作为Android用户&#xff0c;你可能已经注意到了一个问题——Android 13不再支持PPTP协议。但请别担心&#xff0c;作为一家专业的代理供应商&#xff0c;我们将与你分享解决方案&#xff0c;让你轻松解决L2TP问题&#xff0c;享受到高水平的连接体验。本文将为你提供实用的操作…

mysql-事务特性以及隔离机制

一.ACID 事务&#xff08;Transaction&#xff09;是访问和更新数据库的程序执行单元&#xff1b;事务中可能包含一个或多个sql语句&#xff0c;这些语句要么都执行&#xff0c;要么都不执行。 1.逻辑架构和存储引擎 如上图所示&#xff0c;MySQL服务器逻辑架构从上往下可以分…

【C++从0到王者】第二十一站:继承

文章目录 前言一、继承的概念及定义1. 继承的概念2.继承的格式3.继承关系与访问限定符 二、基类和派生类的赋值转换三、继承中的作用域四、派生类的默认成员函数五、继承与友元六、继承与静态成员 前言 继承是面向对象的三大特性之一。我们常常会遇到这样的情况。很多角色的信…

利用OpenCV光流算法实现视频特征点跟踪

光流简介 光流&#xff08;optical flow&#xff09;是运动物体在观察成像平面上的像素运动的瞬时速度。光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系&#xff0c;从而计算出相邻帧之间物体的运动信息的一种方法。…

Kotlin Executors线程池newSingleThreadExecutor单线程

Kotlin Executors线程池newSingleThreadExecutor单线程 import java.util.concurrent.Executorsfun main() {val mExecutorService Executors.newSingleThreadExecutor()for (i in 1..5) {mExecutorService.execute {println("seq-$i tid:${Thread.currentThread().threa…

IDE的下载和使用

IDE 文章目录 IDEJETBRAIN JETBRAIN 官网下载对应的ide 激活方式 dxm的电脑已经把这个脚本下载下来了&#xff0c;脚本是macjihuo 以后就不用买了

Mysql中使用存储过程插入decimal和时间数据递增的模拟数据

场景 Mysql插入数据从指定选项中随机选择、插入时间从指定范围随机生成、Navicat使用存储过程模拟插入测试数据&#xff1a; Mysql插入数据从指定选项中随机选择、插入时间从指定范围随机生成、Navicat使用存储过程模拟插入测试数据_mysql循环插入随机数据_霸道流氓气质的博客…

数据集成革新:去中心化微服务集群的无限潜能

在当今数据密集型的业务环境下&#xff0c;传统的集中式架构已经难以满足高可用性和高并发性的要求。而去中心化微服务集群则通过分散式的架构&#xff0c;将系统划分为多个小型的、独立部署的微服务单元&#xff0c;每个微服务负责特定的业务功能&#xff0c;实现了系统的高度…

SpringBoot基于Zookeeper实现分布式锁

文章目录 问题背景前言实现搭建Zookeeper容器引入依赖ZK客户端的配置类ZK客户端的工厂类注入bean构建测试类 问题背景 研究分布式锁&#xff0c;基于ZK实现&#xff0c;需要整合到SpringBoot使用 前言 参考自SpringBoot集成Curator实现Zookeeper基本操作&#xff0c;Zookeeper入…

AUTOSAR规范与ECU软件开发(实践篇)3.3 AUTOSAR系统解决方案介绍(下)

示例将遵循AUTOSAR方法论来进行开发&#xff0c; 所用的AUTOSAR解决方案如图3.6所示。 图3.6 AUTOSAR系统解决方案 首先&#xff0c; 使用Matlab/Simulink来实现部分软件组件级的开发&#xff0c; 主要包括LightRequestSWC和LightControlSWC&#xff0c; 并自动生成应用层软件组…

基于STM32的ADC采样及各式滤波实现(HAL库,含VOFA+教程)

前言&#xff1a;本文为手把手教学ADC采样及各式滤波算法的教程&#xff0c;本教程的MCU采用STM32F103ZET6。以HAL库的ADC采样函数为基础进行教学&#xff0c;通过各式常见滤波的实验结果进行分析对比&#xff0c;搭配VOFA工具直观的展示滤波效果。ADC与滤波算法都是嵌入式较为…