[Bonjour STM32] No.1-初识CubeMX

大家好。总之这里是被绑在鸽子固定器上抓过来了的泡泡…当了半年多的鸽子,在有史以来最长的寒假里沉迷摸鱼无法自拔整个人逐渐不可抑制地懒散下去了什么的。

​ 有诗云:一月离校六月归,乡音无改视力衰。同学相见不相识,笑问肥宅你是谁(悲)连很少长胖的我自己都在几个月的宅家里伴随着老妈每天都被迫要吃满三顿饭的要求体重增长了十来斤,以至于五一回了趟半年没见的老家都被感叹脸圆了不少。当然躺在家里那能让人无条件放松心情选择睡一觉的床上,自然而然地给自己想出一堆“技术相关的器材都在学校里家里没有条件所以玩不了技术啦,没办法那我就去摸摸鱼刷刷b站吧“的逻辑完善毫无破绽的说辞刷着手机,然后睡前关上机之后心里罪恶感突然冒出来带着明天要开始积极向上好好努力改过自新重新做人的念头入梦,转天起来挂上网课打开QQ开始重复上一天一样循环往复的日常…虽然但是对不起这真的不是我在给我的文章什么的鸽了半年多找借口什么的相信我,我真的有在好好反思了不然你们看有没有从上面这一堆描述里看到这几个月里自己的影子是不是(被夏老师拍飞,over)

​ 好吧进入正题,这个系列教程来源于在我们在群里疯狂摸鱼的时候唯一勤恳敬业的夏老师的一句提议,想着19届这个学年可能就要这么在大家都没有见面的情况下度过整整一个学期了,还记得当初社团招新时候我们厚颜无耻地忽悠学弟进来学习技术,记得承诺每个人进来都能有学长的电子技术教学,记得刚入学的学弟学妹们招新时又热情又向往技术的样子。大一到大二这段时间本该是学长带着学弟们享受社团乐趣的日子,结果疫情来了,和学弟们也半年没有见面没有联系了,承诺好的技术讲座什么的也泡了汤,于是夏老师就提议做这么一个比较特殊的线上教程吧,也当是在这个特殊的时期弥补一下没法在学校见面的无奈啦。本来以我们这些人的鸽子本性可能这个提议被现在提起来之后没准跟大家见面就要鸽到什么期末或者暑假之后了,结果夏老师太熟悉我们的本性了找来鸽子固定器给每个人定好时间表就马上开工,连鸽的时间也没有给我们23333于是这篇教程就这样在这个时间和大家见面啦,说是要说正题结果还是讲了一堆废话,大概也就我是这么啰嗦又无聊的人吧,不过夏老师和子昂他们很显然也不会写这些,所以姑且权当纪念一下这些玩意的来历,以及感谢夏老师的坚持催工和子昂,宋学长等等的帮助啦。我写的这部分没啥技术含量而且大概会比较啰嗦,总之也希望如果能用得上有收获的话就好啦。


​ 好了这次是真的正题了,单片机这玩意应该就不用我再解释一遍了,反正不清楚的话大概也根本不会点进这个教程里来,我们都知道单片机,也就是MCU,微处理器,顾名思义,就是一个小电脑,也就是大家手里大部分傻傻的不太智能的电子产品的那个脑子,因为是小电脑比较傻转起来不太快而且也写不下几句代码所以这些电器用起来才基本都傻傻的,哪怕这些东西现在大部分都喜欢在名字前面加上“智能”两个字来蹭下人工智能的热度什么的。不过不管怎么说,虽然傻,有脑子起码还是比没脑子的要好很多的对不对,至少当你早上被吵起来想睡回笼觉的时候拍一下那个有点脑子的闹钟,他的那几句程序还是知道这时候要停下来,不至于让你气的跳起来把它给砸了,这就是那一小片单片机的功劳了。

​ 再说我们这次的主题STM32,还是顾名思义,STM是ST Microelectronics的缩写,也就是st微电子,st就是咱们电子行业里大名鼎鼎的意法半导体,也算是师出名门。至于32呢,就是32位单片机的意思,这个32位的位是什么概念,解释起来大概会有点复杂,涉及到数电和微机原理之类的一些东西,这里先揭过不提,如果以后不鸽的话做进阶些的单片机教程会详细说明这个事的。但是基于众所周知的理解,在电子产品这里,大的就是好的适用于大多数情况,i7比i3厉害,1080ti比1050厉害,内存越大越厉害,硬盘越大能收藏的不可名状资源越多,所以顺理成章,32位机自然也要比16位机和8位机,4位机厉害到不知道哪里去了。为啥是这几个数?二进制嘛,2的整数次幂。记住,IT从业者的自我修养之,数数从0数,1000不是整数,1024才是。哦,对了,你或许还听说过64位机,或者x64系统之类,家里的电脑和近几年的手机,已经全面普及64位CPU和配套软件了,这也就是你家的电脑和手机比我们这里的这个32位机的单片机强大的地方啦。

​ 那么有了单片机,自然就需要程序,单片机开发所用的编程语言,就是咱们大一刚开学马上就开课,成了很多人的心理阴影的C语言啦,这个历史比家用PC机还要长的古董语言,因为对涉及硬件层面的底层编程的高效性,至今还是单片机编程当之无愧的首选语言(如果把编程语言娘化一下的话大概就是???岁的出道即巅峰辉煌一生永远不老万能的万年萝莉吧划掉)所以你们不用担心又要学一遍什么其他的莫名的复杂难懂的编程语言了,也明白为什么2020年了大一不教又简单又好玩又时髦还能蹭蹭AI热度的Python而是还抱着C这个又老又不可爱的老古董了吧,这是咱们电子从业者,搞硬件开发的命啊。Python或许会随着AI热度的下降而不那么流行,但是C在单片机开发领域的地位在可见的未来几乎不可动摇。好吧这里有个坏消息,毫无疑问的,在单片机开发里,C的那个让你们大一最头疼的玩意,指针,是绝对的灵魂(悲)。当然,后面你就知道,这玩意其实也挺简单的就是啦。

​ 如果这半年的超长寒假还没有让你们把C语言连带他的第一个hello world都一起还给谭浩强的话,应该都很熟悉一个C程序应该要怎么写,一个main函数,程序从这开始,一行行傻傻的的往下跑,不会回头。输入输出什么的,在电脑里不用担心,两个所有人都会的stdio.h里的函数包办了所有功能,只要写好了main,点一下软件上面的那个绿色小箭头——“Run运行”,然后看着一个黑框框里面显示的字就一切ok了。

​ 但是到了单片机,问题或许就复杂了一些,很显然,不管是你从淘宝上买来的X合一附带N多说着十天学完实际上用不了三天就劝退弃坑了的视频教程的开发板,还是上面那黑不溜秋一堆腿的一小片长的或者方的玩意,都没有给你配套一个27寸大屏或者原装黑轴游戏键盘来画黑框框用来printf或者scanf,我们怎么和单片机沟通呢?

​ 首先,像之前说的,单片机是个很傻的电脑,记不下几句代码,自然也认不得那些高清大屏,那我们就只能从信息时代多媒体时代一路退回法拉第和爱迪生的年头了,用些原始的办法——初中电学实验课,灯泡,喇叭和电机,一条电线拉过来的电报。一眼就知道,单片机有很多腿,当然都是一根根电线,给这些腿接上几个LED发光二极管,还有简单的开关按钮,按一下开关,单片机就知道你输入了一个信号,单片机收到了,在接着LED的另一条腿上输出一个电压,LED亮了,你也就知道单片机收到了,这就是输出了一个信息。很傻,看起来很落后,似乎很像什么点个狼烟给远处的军队报个信这种上古时代的原理,但是简单实用。拍一下闹钟的开关,他不响了,这也是一个开关,一个输出,灯泡换成了喇叭,开换成了关,这种傻傻的智商,很多时候就是单片机要做的了。其实,我们电子工程师要做的,很多时候也根本没有“人工智能,机器学习,计算机视觉”这些高大上的名词和概念,就是这么一些傻傻的,但是实用的东西。

​ 好了,铺垫了这么多,终于可以开始讲单片机编程了,我们要做的,说起来其实很简单,就是在C语言的那个main里,告诉单片机怎么控制他的那么多引脚,哪里应该输出高电平,哪里应该输出低电平。一个能编程输出高电平和低电平的单片机引脚,就是每个单片机里最基本的功能,我们入门所有单片机首先需要掌握的,叫做GPIO的东西(General-purpose input/output,通用输入输出)。而这也是单片机最基本的一种外设,外设这个词,和电脑的外设一样,就是用来实现各种各样的功能的外置组件啦,外设集成于单片机之内,和单片机核心一起组成了单片机这个整体,而单片机核心和单片机外设的关系,就像是人的大脑和四肢,五官的关系一样,一个负责控制和思考,一个负责对外交流和具体的各种工作,缺一不可啦。

​ 我们知道,外设,或者举个例子,我们第一个要学习的GPIO,单片机IO口,是一个可以编程控制它的输出状态的器件,抽象化描述一下,就像一个变量,可以用代码随意的改变它的值,也就是主动控制这个IO口的状态,或者读取它的值,以此来得知他现在的状态。

​ 最简单的,我们把单片机的第一个引脚上接一个LED灯,然后给他取个名字,就叫LED1,那么我们对他赋值LED1=1;,也就是告诉单片机,让这个引脚的输出为1,也就是高电平,那么灯就亮了,这就是最简单也最好理解的GPIO控制,写成完整的代码,大概就长这样:

sbit LED1=P1^0;

LED1=1;

​ 实际上,其实这就是最原始的51单片机的IO口编程写法,就长这么个样子,如果有人自己学过51单片机的话,应该印象很深。其实,sbit这个大一学的标准C语言里没有的词,代表的意思就是把LED1这个变量名,和单片机硬件上的第一个IO端口关联起来,控制这个变量,就代表控制了单片机的这个端口,而这个特殊的变量,就被叫做单片机的寄存器,也就是在单片机里具有特殊内存地址的变量,通过在程序里读写它的值,就可以操控单片机对应的外设的具体状态啦。这里我们复习一下指针的概念,存储变量内存地址的变量,对不对,所以这里你应该就明白为什么单片机开发里指针是相当重要的技术了。

​ 那么接着说,上面说51是一个非常原始的单片机,它只是个8位机,而我们这里要学的STM32是个32位机,他们的性能可不只是相差了区区4倍,而是相差了几十年技术的时代之差——51单片机大概已经是比Windows操作系统出现还要古老的技术了。STM32相对于51的强大是方方面面的,当然不只是最简单的单片机核心算的更快,内存更大,能装的代码更多,在外设上他也比51简陋的IO控制器先进了几个时代。51单片机的IO口只能简单的输出高电平和低电平,或者读取上面的电平是高或者低,而STM32的一个IO口确可以控制输出的速度、默认状态的高低、输出方式的不同,甚至用PWM技术实现可调的输出大小,或者用内部集成的ADC读取输入信号的电压高低…大概就是一个是诺基亚一个是iPhone11的功能差距吧。然而…坏消息又来了,先进的东西都比较复杂,就像咱们小学的数学只需要四则运算,但是高数的微积分能让万千大学学子恨不得把牛顿的棺材板从土里刨出来,所以说微积分是个跨时代的先进发明,这个多功能的IO口,这么多功能自然也需要各种各样的变量,也就是寄存器,去控制它的功能。于是,如果我们还用刚才的这种最简单的技术去控制一个IO口,代码就变成了这个样子:

void LED_Init(void){
    RCC->APB2ENR|=1<<3;
    RCC->APB2ENR|=1<<6;

    GPIOB->CRL&=0xFF0FFFFF;
    GPIOB->CRL|=0x00300000;

    GPIOB->ODR|=1<<5;

    GPIOE->CRL&=0xFF0FFFFF;
    GPIOE->CRL|=0x00300000;

    GPIOE->ODR|=1<<5;
}
int main(void)
 {  
    LED_Init();
    delay_init();
    while(1){
        GPIOB->ODR|=1<<5;
        GPIOE->ODR|=1<<5;

        delay_ms(500);

        GPIOB->ODR&=~(1<<5);
        GPIOE->ODR&=~(1<<5);

        delay_ms(500);

    }

​ 是不是有种:很简单吧,那么让我们做下练习题吧.jpg这样的感觉。实际上,我承认,这些破玩意我自己也看不懂,是我从Google上刚刚抄来的,所以目的仅在于作为反面典型,告诉你们不要学这个而已...总之我们的故事继续,可以看到当外设的功能多起来了之后,寄存器的数量和复杂度都会上升到一个很夸张的程度,导致普通工程师的入门学习和编程开发难度都相当的劝退...毕竟你们看见这个肯定也劝退了。哪怕不是STM32这个先进的32位机,而是更早的一些16位机上,这个问题就已经开始显现了。厂家一想这样不行啊,我们好不容易给这些单片机做出来这么多花里胡哨的新功能,但是因为写起来太麻烦工程师都不愿意学所以卖不出去那不是亏大了,于是他们就整出来了这么一个东西。库函数。

​ 这玩意呢,其实很简单,和咱们学C语言的时候调用过的头文件是一样的,头文件就是C语言的库,也就是引用一段别人写好了的程序,那些复杂的过程已经有专业的厂家程序员帮忙写好了,我们只需要做的是调用对应功能的函数,然后库就会帮你完成剩下的东西。就像我们用电脑C语言算sin,只要包含这个头文件,然后调用sin这个函数,具体的算法已经有人帮你写好了。而库函数的名字和调用参数用法之类的自然不像上面的寄存器那样都是一个个抽象的二进制数值,而是包装好了的简单易懂的名称,于是用库函数写一个STM32的IO口控制看起来就比上面那个要像人话多了:

void led_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PA端口

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PA.8端口设置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口的速度为50MHZ

    GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化PA
    GPIO_SetBits(GPIOA,GPIO_Pin_12);//PA.8输出高
}
GPIO_SetBits(GPIOA, GPIO_Pin_12); *//设置 GPIOA12 输出 1,等同 LED=1;    

GPIO_ResetBits (GPIOA, GPIO_Pin_12); *//设置 GPIOA12 输出 0,等同 LED=0;

​ 是不是看起来起码还能认识点英文,至少知道大概说的是啥意思了。啥?你说这破玩意大概就是从量子物理简化成高等微积分这种程度,没什么用还是看不明白,还是比上面那个LED1=1麻烦太多了?那没办法,毕竟功能这么多,总是要告诉单片机这些功能都要怎么用的,这就是功能复杂了的坏处。至少这东西已经比上面那个要亲民得太多了,熟练之后已经是可以拿来比较有效率的手工编程的了。于是库函数这种东西就作为大部分新型单片机的通用编程方式被沿用至今。

​ 当然,上面的寄存器式开发也有着自己的优势,也就是可以自由的控制单片机外设的每一个细节的动作,熟练之后非常灵活,而省去了函数的包装,代码运行的速度也是比库函数要更快一些的,于是仍然有一些老工程师或者单片机性能的追求者钟情于传统的寄存器开发,两者谁也没法取代谁。于是你去查STM32开发教程,经常能看到大概十来年前的时代出品的教学书籍,同时具有两个版本——库函数版和寄存器版。

​ 但是是不是觉得还是有些不完美?如果我说下面就要开始仔细讲解上面这一堆代码该怎么用你们会不会想要直接点击右上角?大概ST也是觉得这玩意确实还是麻烦了点,劝退的人还是多了点,于是他们想来想去,把上面那个库函数又各种魔改了一下,然后创造出了一个神奇的玩意,STM32CubeMX,也就是我们真正的主题啦(看一眼统计5k多字了,总算是聊回来了...)
YYcPVe.jpg

​ 这个就是STM32Cube系列的构成啦,这玩意具体的说其实是ST公司为了简化单片机开发,就自己做了一整套用来进行STM32开发的软件架构系统,从集成开发环境IDE到库函数包到下载器编程器,全都是ST自家的(这就叫大公司,有钱任性,啥都做自己的)。而我们这里要讲的,就是这里面的STM32CubeMx,也就是STM32单片机自动配置和项目代码生成工具。(至于其他的部分,一个是开发还不是那么完善,一个是有成名已久的更好用的替代品,所以这里不用他们)

​ STM32CubeMX这个东西,说简单点,就是把你们手算微积分这件事再简化一遍,变成软件帮你算微积分,然后直接告诉你结果,没错,就是这么夸张的傻瓜化级别。CubeMX的实现实际上还是基于库函数,但是使用上更加简化,这玩意全程用图形界面操作,不用写一句代码,就可以简单的选择你的单片机,然后设置好你需要的每个外设的功能,还有单片机核心的性能参数设置之类,帮你把这些配置代码全都写好,库文件也按需求准备好,点一个按钮,直接生成能用集成开发环境写代码的工程文件,工程目录下的文件结构都配好了,接下来需要做的,就是自己控制你需要的外设就好了——这里真的回到51的那句了,又变成只需要一行代码就行了。

​ 于是我们这个教程的内容就已经确定下来了,基于STM32CubeMX,选用的集成开发环境IDE是经典的Keil,而这里用作例子的单片机就是最经典的STM32F103C8T6,最便宜的淘宝爆款核心板,几块钱一片,关于这个单片机具体的内容以后会讲。这里我就当你们已经按照夏老师前面的教程安装好了这些所需的软件和环境(啥,你说夏老师的教程还没出?那去催他,不关我的事啦)

YYcHQP.png

​ 那么开始吧,打开这个软件。(我得吐槽下这个软件是用java写的,比较卡,尽量忍耐一下吧,经常容易迷之卡住)

YYgCQ0.png

​ 这个就是主界面啦,勉勉强强还能算得上清爽好看吧大概(至少相比后面要用到的Keil要好看的多了)

YYgSWn.png

​ 然后当然是新建项目,这个英文菜单应该还算得上是简洁易懂啦,我们选File-New Project

YYczJs.png

​ 如果以后你做开发的话绝对会深恶痛绝的万恶之源...这里是在同步ST服务器里的单片机数据,然后,由于物理上的原因,ST的官网当然在国外,所以这个服务器就比较的卡...没事,忍耐一下就过去了,也不会太慢的,一般来说,或许,大概(如果真的卡了那就玩会手机水水群吧)

YYgpzq.png

​ 这里就是选择单片机型号的界面啦,STM32的产品线很多,这里的筛选方式也很丰富,可以从各方面的参数和功能来选型你需要的型号,右边还能直接看到片子的简介描述和datasheet啥的,不过我们这里直接在左上角输入我们需要的STM32F103C8,然后选中它,双击一下,或者点一下右上角那个蓝色的按钮,项目就创建好了

YYgwOf.png

​ 这个就是配置我们的单片机时候的主界面啦,右边的这个就是对应的实际单片机的引脚图,上面有颜色的是已经分分配使用了的引脚,现在还没开始配置功能的时候显示的都是一些不能被占用的电源脚之类,灰色的就是可以被配置为外设功能的可用引脚啦。左边的下拉菜单自然就是选择的这片单片机所具有的外设功能了。

​ 这里作为入门篇,首先主要是介绍一下STM32CubeMX这个软件大体上的使用流程,还有就是任何一个STM32开发都需要的共通设置,所以就先不讲具体的外设配置了,控制GPIO点一个LED灯单片机开发届地位等同于hello world的这种经典任务就留在下一节正式开始了。

YYgykQ.png

​ 首先我们点SystemCore下拉菜单,顾名思义,这里是单片机核心的最重要的一些设置,其中绿色的是因为默认已经有了一些功能被配置开起了,而黑色的是还没有开启的功能。这里点击RCC,右边的窗口出现RCC对应的外设设置,上面的是基本的功能选择,下面的窗口是具体的参数设置。

YYgrTg.png

​ 这里我们不用管下面的窗口,把上面的窗口的HSE这个下拉菜单改成里面的Crystal Resonator。HSE也就是系统的高速时钟,也就是决定单片机运行速度的核心时钟,而我们选择的crystal就是单片机外接石英晶体振荡器作为高速时钟源的意思,而默认的Disable则是使用单片机自带的内部振荡器作为时钟源。

​ 外置的晶体振荡器频率稳定性相对于内部集成的要更好,而我们这片核心板也安装了外置晶振,所以这里选择使用外接时钟。

YYg4mT.png

​ 然后点击SYS菜单,把Debug这个下拉菜单设置成SerialWire,也就是串行调试。这个设置是单片机的下载和调试接口,我们使用ST官方推荐的STlink调试器进行下载和调试,可以一键自动下载,比起51常见的需要手动开关机下载程序要方便很多,而且速度也很快。另一点是,这个接口只需要核心板和调试器之间连接四根线就可以使用。

YYgTk4.png

​ 其他的外设设置我们先不讲,直接点击上面的Clock Configuration,进入时钟配置页面。

​ 是不是吓了一跳?这个看起来很复杂的玩意就是STM32单片机著名的时钟树系统,也是曾经劝退无数人入门学习的第一课——毕竟配置不好时钟,单片机就是一块废铁。当然现在我们有了CubeMX,最大的好处之一就是,再也不用手动去配置这个复杂的时钟树了,软件可以自动帮我们把一切都配置妥当。

​ 这里的左下角可以看到,默认的HSE外接晶体振荡器频率和我们核心板上的是一样的,8MHz,不需要改动。而我们需要关注的就是中间的那个框框里的数值,HCLK,简单说,也就是我们单片机这个电脑的CPU主频,直接决定了单片机运行的速度。

​ 不像51单片机那样,时钟频率直接等于外置晶振频率,想要更改时钟主频只能硬件更换晶振,STM32这样新一代的单片机可以通过程序设置来在单片机内部修改时钟频率。而在输入框的下面可以看到写清楚了,这片STM32F103C8T6的最高支持主频是72MHz,可以看到现在软件默认配置的速度只有8MHz。众所周知,主频速度几乎和单片机运行速度成正比,那么一般情况下,我们当然是希望他工作的越快越好,所以我们这里直接把这个框里的数值改成72,然后敲下回车键吧。

YYgz7D.png

YYgO6x.png
软件提示了现在的时钟配置不满足要求,那么我们点ok,让软件帮忙自动配置
YYgXX6.png

​ 这样我们的时钟树就配置完成了。是不是很傻瓜式。

YY2pAe.png

​ 接下来就是第三个选项卡,也就是项目工程的设置了。这里最重要的就是项目名称和存储目录了,自己选择一个名字和保存项目的文件夹就好了。这里注意,CubeMX不支持中文目录名,因此一定要注意路径名全英文

YY2Chd.png

​ 设定好保存目录和项目名

YY2i9A.png

​ 然后选择使用的IDE,我们选MDK-ARM,也就是著名的Keil

YY2F1I.png

​ 剩下的选项我们保持默认,于是这个空白工程配置完成,可以点击右上角的GenerateCode按钮生成工程文件了。

YY2kct.png

​ 生成代码需要一点时间,而如果你是第一次使用cubeMX的话,软件需要从ST服务器上下载你选择单片机的整个库函数固件包,于是就又要见到上面那个深恶痛绝的下载提示了...而且这个包相当的大,几百个M的大小,所以一般下载过程都会比较的煎熬...所以还是那句话吧,耐心是很重要的,不过如果没有的话,你还有游戏,QQ和完全正当的摸鱼借口23333

YY2AjP.png

YY2eHS.png

​ 工程生成完成,点击打开工程,如果你已经安装好了Keil,那么就能看到配置完成的工程了。

YY2uNQ.png

​ 打开左边的工程目录,可以看到生成好的各个代码文件和库文件的目录结构。

​ 那么到了这里,就已经完成了一个STM32开发的基本工程配置了,剩下的就是打开你需要用的每个外设,以及在IDE里实现自己需要的功能代码了。下一节,就是实际讲解怎么超简单的点亮一个LED灯了。

​ 我们后面再见。这篇教程一如既往的被我啰嗦了好多的废话,一不小心就说了很多乱七八糟的东西,感谢大家能耐心的看完,这里是被逼着不敢摸鱼了的泡泡,谢谢支持啦,如果能喜欢就最好了。

发表评论