本文是我参加蓝桥杯嵌入式比赛后的一些心得体会和一些自己总结的驱动代码,希望能给以后参加蓝桥杯嵌入式的同学带来一些帮助。
本文没有经过校对,如有错误还请包涵,欢迎大家交流和指正,转载请注明出处。
一、 总述
首先说一下自己的情况:
我参加的是第九届蓝桥杯嵌入式比赛
省赛备赛两周(平均每天花费4——6小时),最后省一进入国赛
国赛备赛一周半(平均每天花费4——6小时),最后国二
我是计算机科学与技术专业的学生,对模拟电路知识有所欠缺,所以客观题模拟电路部分只能看脸。
之前有过使用stm32做小项目的经历可以快速上手(看到这里不要急,拿不拿奖与有没有stm32使用经历没有太大关系)
再谈谈对蓝桥杯嵌入式比赛的认识:
简介套话不说,这个比赛我个人认为得奖很容易,但想拿国一及以上就有些难度了,需要一定的训练加一定的运气。国二以上基本都是在客观题上分高低。
比赛考察范围有限且基本上都是套路,有限的知识和套路全部掌握了就能拿到国二,我也会在接下来的文章里把所有要掌握的点全部给大家梳理清楚
这个比赛主要针对的是电子工程类专业学生和计科学生,如果不是这些专业的学生就需要补充一些基础知识(C语言、微机原理、数电模电…)
比赛还是主要面向双非学校学生,主要考察对stm32的使用(毕竟占总成绩70%的客观题就是写个小程序),其次在客观题会考察一些stm32、Cortex-M3、嵌入式、数电模电的一些基础知识
我将会总结什么:
- 如何构建工程
- 我认为必须掌握的驱动及其运用
- 怎么通过比赛提供的资料迅速完成我们的程序
- 我对考察点的个人心得,以及怎么分配我们的备赛时间
- 客观题的准备
PS:
这个比赛最大的优点也是最大的缺点便是:考察范围有限
看完这篇文章并且掌握我所提到的东西,至少能拿省一。欢迎交流与分享,切勿商用哦。
二、比赛提供
所有童鞋一定会问到比赛时提供什么,这一点官网也没有做很好的说明,在这里我说明一下:
2.1 省赛提供
2.1.1 省赛硬件方面
第八届和第九届的省赛都是只提供竞赛板,不涉及扩展板内容
2.1.2 省赛软件方面
省赛提供资料图:
-
CT117E(就是竞赛板)电路原理图和竞赛板使用说明
比赛时如果忘记板上资源对应的引脚,电路原理图用来查看对应引脚。
-
串口调试工具
如果考察到串口(UART),需要用调试工具接收和发送来验证程序
-
驱动及插件
FT2232驱动和CooCox驱动,这些都用不到,不要管它
-
数据手册
数据手册资料图:
这些数据手册各有各的用处,我认为最有用的是stm32f103rbt6.pdf,怎么看数据手册以及什么时候看后面会讲。
- 液晶驱动参考例程
液晶驱动参考历程资料图:
一个已经写好的工程CT117E-LCD: 这个工程可以作为我们进考场时测试板子及其屏幕是否正常工作,
也可以直接在这个工程上构建我们的程序,省略了我们重新建工程的时间(我选择重新构建工程)
lcd驱动文件:一个.c源文件和两个.h头文件,这三个就是lcd的驱动文件,后面会讲到如何使用它们
-
i2c参考程序
只有i2c.c和i2c.h两个文件,只在E2PROM和陀螺仪的时候能用到,后面会讲如何使用
-
STM32固件库v3.5
固件库资料图:
这个固件库十分关键,一方面要用来构建工程,一方面又能利用库函数和里面的Example快速编写驱动
2.2 国赛提供
在硬件上:竞赛板+扩展板
国赛在省赛提供的所有资料的基础上,增加了:
-
扩展板的相关数据手册和电路原理图
-
DS18B20和DHT11的驱动
只有.c和.h文件,没有工程和demo
三、构建工程
万里长城第一步,先把stm32工程给建立起来,构建工程需要用到比赛提供的STM32固件库和keil
keil的安装请参照网上的各种教程,多的数不清~
3.1 建议使用keil4
这里需要特殊说明一下,比赛时提供的是keil4,而如果你平常训练使用keil5的话主要有两点不同:
- keil4没有代码自动联想补全功能,所以如果你使用keil5进行平常联系,我建议你关掉代码联想功能。
- keil4在设置那个Colink下程序进板与keil5有些许的差别,我下面在构建工程时会讲到如何设置。
综上所述,我建议在平常联系就跟比赛时一样采用keil4,避免一些不必要的麻烦。
3.2 构建工程
首先在先在一个目录下(磁盘上的任何地方)建立一个文件夹
可以看到我在C盘目录下建立了一个名叫test的文件夹
之后在test文件夹下建五个文件夹
- CORE: 用来存放内核文件
- FWLIB:用来存放固件库的源文件和头文件
- HARDWARE:用来存放驱动程序代码
- OUTPUT: 用来存放在编译过程中产生的中间文件
- USER: 用来放用户文件和main函数文件,以及把工程项目建立在此处
接下来便是往这五个文件夹里copy东西了,打开比赛提供的STM32固件库V3.5
可以看到在文件夹“STM32F10x_StdPeriph_Lib_V3.5.0”的内容有:
给CORE文件夹里添加文件
- 将Libraries->CMSIS->CM3->CoreSupport文件夹下的“core_cm3.c和core_cm3.h"文件放入CORE文件夹内
- 将Libraries->CMSIS->CM3->DeviceSupport->ST->STM32F10x->startup->arm文件夹下的"startup_stm32f10x_md.s"放入文件夹内
最终CORE文件夹内的文件有:
给FWLIB文件夹里添加文件
- 将Libraries->STM32F10x_StdPeriph_Driver文件夹下的“inc文件夹和src文件夹”一起放入FWLIB文件夹内
最终FWLIB文件夹内的文件有:
给USER文件夹里添加文件
- 将Libraries->CMSIS->CM3->CoreSupport->ST->STM32F10x文件夹下的“stm32f10x.h、system_stm32f10x.c和system_stm32f10x.h”放入USER文件夹内
- 将Project->STM32F10x_StdPeriph_Template文件夹下的“main.c、stm32f10x_conf.h、stm32f10x_it.c.c和stm32f10x_it.h”放入USER文件夹内
最终USER文件夹内的文件有:
打开keil4,新建工程
点击keil4中Project->New uVision Project:
将工程建立在我们的test->USER文件夹下,命名为test:
芯片选择为STM32F103RB:
添加工程文件
点击这个组建来修改和添加目录和文件:
将Project Targets修改为"test",test下增加4个Groups分别为"CORE"、“FWLIB”、“USER”、“HARDWARE”:
点击Add Files,把CORE文件夹下的core_cm3.c和startup_stm32f10x_md.s添加到CORE:
把USER文件夹下的 main.c、stm32f10x_it.c和system_stm32f10x.c添加到USER:
FWLIB里面添加的是需要用到的库函数源文件,可以全部添加也可以只添加要用到的。我在这里把FWLIB->src文件夹下所有库函数源文件都添加进来(其实没有必要,但也仅仅是编译时慢了些):
此时,可以看到keil4里左侧的项目目录已经发生了变化
打开main.c把里面所有文件清空
写下以下代码:
int main()
{
while(1)
{
}
}
写过嵌入式代码的童鞋应该对这两行代码并不陌生,main.c 文件最终样式:
对工程进行相关设置
点击Target Options…,进入工程设置的组件:
在Output中点击"Select Folder for Objects…":
选择OUTPUT文件夹:
在C/C++中的Define里填写
USE_STDPERIPH_DRIVER,STM32F10X_MD
- 这一句话可以背过也可以在stm32f10x.h的95行找到:
填写后效果为:
随后点击Include Path的选项增加头文件路径,分别添加CORE、FWLIB->inc、HARDWARE、USER:
在Debug中点击右边的Use并选择CooCox Debugger:
点击Settings,并在Debug选项里选择Adapter为Colink:
再在Flash Download里点击Add,添加“STM32F10X Med-density Flash”:
最后一步也是keil4和keil5有区别的地方,keil4需要在Utilities中选择CooCox Debugger(这一步keil5不需要):
设置完后,编译程序并下板子
这下全部都设置完了,点击OK后,点击Build会得到 “0 Error(s), 0 Warning(s)”:
将生成的可执行文件下到板子里点击Download组件(如果下板子失败,把工程关闭重新打开,keil4有bug):
至此一个工程已经全部构建完毕
3.3 对于构建工程的个人建议
建议自己构建工程
你可以选择直接使用比赛时提供的液晶驱动例程,也可以选择自己构建工程。
我建议自己构建工程,个人认为液晶驱动例程的结构不清晰,自己构建的用着顺手。
驱动文件编写和存放
我把所有驱动文件都存在HARDWARE文件夹下。
每次新建一个文件,保存为.c或者.h文件都存在HARDWARE文件夹里,并在main.c中调用。
比如写LED的驱动,点击New就新建了一个文件,系统自动命名为"Text1":
接下来点击Save,将文件命名为"led.c"并保存到HARDWARE:
同样的再创建一个新文件,命名为"led.h"并保存到HARDWARE,双击左侧Project里的HARDWARE将"led.c"添加进工程:
添加完后可以看到左侧Project里的HARDWARE目录下出现了"led.c":
接下来便是编写"led.c"和"led.h"代码,之后在main.c里include "led.h"来使用。具体的驱动代码在后面会讲。
建议经常练习建工程
我建议每次练习一个题目都重新构建一遍工程,毕竟孰能生巧,在比赛时最好不要在准备工作上出问题。
四、快速编写必备驱动
4.1 LED
LED驱动其实就是单纯对GPIO进行操作。
但是这里加入了一个74HC573作为数据锁存器,所以每次操作需要给PD一个下降沿。
4.1.1 LED硬件连接
LED | STM32 |
---|---|
LED0 ~ 7 | PC8 ~1 5 |
LE | PD2 |
4.1.2 LED软件驱动
led.c:
#include "led.h"
void led_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED_ALL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
void led_Control(u16 led, u8 state)
{
if(state==1)
{
GPIO_ResetBits(GPIOC,led);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
else if(state==0)
{
GPIO_SetBits(GPIOC,led);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
}
}
led.h:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#define LED_ALL GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15
#define LED1 GPIO_Pin_8
#define LED2 GPIO_Pin_9
#define LED3 GPIO_Pin_10
#define LED4 GPIO_Pin_11
#define LED5 GPIO_Pin_12
#define LED6 GPIO_Pin_13
#define LED7 GPIO_Pin_14
#define LED8 GPIO_Pin_15
void led_Init();
void led_Control(u16 led, u8 state);
#endif
4.1.3 LED驱动使用
在main.c文件中包含led.h头文件:
#include "led.h"
在main函数初始化阶段直接调用LED初始化函数初始化LED
int main()
{
...
led_Init();
...
while(1)
{
...
}
}
如果想点亮LED1则直接调用:
led_Control(LED1,1);
如果想让LED1~8全灭则调用:
led_Control(LED_ALL,0);
4.1.4 利用库函数的Example快速写LED驱动
LED的初始化驱动代码其实就是初始化了GPIO,设置对应引脚为推挽输出
LED的控制驱动代码就是对GPIO进行高低电平的输出
全是调用库函数来实现的,我们可以不必完全背过每个库函数的具体写法,直接从相应的库的Example里复制粘贴即可,大大减少比赛时间。
在比赛提供的V3.5库的"Project->STM32F10x_StdPeriph_Examples->GPIO->IOToggle"文件夹下,打开"main.c"
可以看到里面有基本上所有LED初始化驱动所需的代码,直接复制,改改对应引脚和对应使能时钟即可:
- 增加使能时钟GPIOC
- 增加设置GPIO_Pin_8 ~ GPIO_Pin_15为推挽输出
设置引脚高低电平的函数可以在keil左侧的Project里找到"stm32f10x_gpio.h",打开后拉到文件最底找到:
如果快速找到这些代码,就可以大大节省敲代码所花费的时间,把更多的时间留在逻辑层,增加得奖的可能性
我个人的原则就是,能复制粘贴解决的事就不自己敲,因为这是比赛,有时间限制,要让自己得奖可能最大化
4.2 SysTick来实现延时函数
为什么要先说延时函数呢?本来是要说按键驱动的,而按键我使用外部中断,利用延时函数来消抖,还是先说一下延时函数。
4.2.1 毫秒延时函数
stm32f10x_it.c:
u32 delaytime;
void delay_ms(u32 time)
{
delaytime=time;
while(delaytime!=0);
}
...
void SysTick_Handler(void)
{
delaytime--;
}
直接在stm32f10x_it.c文件中写上述代码,SysTick_Handler()的空函数在stm32f10x_it.c文件底部:
4.2.2 更改SysTick_Config
因为之后要用到我们的延时函数来打断外部中断对按键进行消抖,所以要更改SysTick_Config的中断优先级。
然而SysTick_Config()函数在内核文件"core_cm3.h"中:
比赛规定内核文件是不允许被修改的,我们直接把这个函数复制出来放到main.c中,重新命名为"SysTick_Config1()"
将这一行代码:
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
修改为:
NVIC_Se