Keil5开发环境搭建:为嵌入式GLM-OCR前端设备编写固件

最近在做一个挺有意思的项目,需要给一个嵌入式设备写固件,让它能控制摄像头拍照片,然后通过串口把照片数据传给电脑上的AI模型做文字识别。这个设备的核心是一块STM32芯片,而给这类芯片写程序,Keil MDK算得上是工程师们的老朋友了。不过对于刚接触的朋友来说,从零开始搭好这个环境,可能还是会遇到些小麻烦。今天我就把自己搭建Keil5环境,并创建一个基础固件工程的过程梳理一遍,希望能帮你省点时间,快速上手。

整个流程走下来,其实就几个关键步骤:先把Keil5软件装好,然后下载对应芯片的支持包,接着新建一个工程,写点简单的驱动代码,最后把程序烧录到板子上跑起来。我会尽量把每一步都讲清楚,特别是那些容易踩坑的地方。

1. 准备工作:明确目标与获取资源

在动手安装之前,我们先花两分钟把目标和需要的“食材”搞清楚。我们的目标是搭建一个能用来开发STM32等ARM Cortex-M系列芯片固件的环境。这个固件将来要干两件核心的事:一是控制摄像头模块采集图像数据,二是通过串口与上位机(比如运行着GLM-OCR模型的电脑或树莓派)进行通信。

所以,我们需要准备以下东西:

  • Keil MDK5 安装包:这是集成开发环境(IDE)本身,包含了编辑器、编译器、调试器。你需要去ARM的官网注册并下载评估版,对于学习和非商业用途是免费的,功能齐全只是代码大小有限制。
  • Device Family Pack:也叫芯片支持包。Keil本身不带具体芯片的底层驱动库和头文件,你需要根据自己使用的STM32型号(比如STM32F103、STM32F407等)下载对应的包。
  • STM32固件库或HAL库:虽然芯片包里有基础定义,但为了更方便地操作外设(比如GPIO、串口、定时器),我们通常使用ST官方提供的库。新手可以从标准外设库(StdPeriph Lib)入手,它更贴近寄存器,易于理解;项目开发更推荐用HAL库,抽象程度高,移植方便。
  • 一款STM32开发板:手边得有块硬件来验证程序。像STM32F103C8T6(蓝色小板)这种就非常常见和便宜。
  • 一个ST-Link调试下载器:用来把编译好的程序烧录到芯片里,并支持在线调试。这是必不可少的工具。

把这些东西的下载链接都准备好,放在一个你能找到的文件夹里,我们就可以开始了。

2. 一步步安装Keil MDK5

安装过程本身并不复杂,跟着向导点“下一步”就行,但有几个选项需要注意,选对了能避免后续的麻烦。

首先,运行下载好的MDKxxx.exe安装程序。建议暂时关闭杀毒软件,以免某些组件安装被拦截。

第一步:选择安装路径 安装向导启动后,会让你选择安装路径。这里有个小建议,路径里不要包含中文或空格。比如,你可以安装到 C:\Keil_v5 这样的目录下。使用纯英文路径能最大程度避免编译器或链接器在某些情况下出莫名其妙的错误。

第二步:填写用户信息 接下来会要求你填写姓名、公司名和邮箱。评估版可以随便填写,但如果你有正版许可证,这里的信息需要和许可证对应。

第三步:安装组件 这一步是关键。安装程序会列出可选的组件,通常包括:

  • ARM Compiler:这是核心的C/C++编译器,必须安装。
  • MDK Core:IDE的核心组件,必须安装。
  • 各种中间件和软件包:比如RTOS内核、文件系统、网络协议栈等。对于初学者,可以先不勾选,等需要的时候再通过包管理器在线安装,这样能保持安装包的轻量。

第四步:等待安装完成 点击安装后,就泡杯茶稍等一会儿。安装完成后,可能会提示你安装某些设备的驱动(比如ULINK调试器的驱动),如果你使用的是ST-Link,这里可以先跳过,我们后面会单独安装ST-Link的驱动。

安装完毕,桌面上会出现“Keil uVision5”的图标。先别急着打开,我们还有重要的一步。

3. 安装芯片支持包与STM32库

Keil安装好后就像一个空壳,它还不知道怎么对付你的具体芯片。这就需要安装芯片支持包(DFP)。

3.1 在线安装芯片包(推荐) 打开Keil5,在菜单栏找到 Project -> Manage -> Pack Installer。或者直接点击工具栏那个有点像“小盒子”的图标。这会打开“Pack Installer”窗口。

在“Packs”页面,你会看到一个列表。在搜索框里输入你的芯片系列,比如“STM32F1”,它就会列出STM32F1系列的所有芯片包。找到和你开发板芯片型号匹配的那个(例如STM32F103系列),点击右侧的“Install”按钮。Keil会自动从服务器下载并安装。这种方式最省心,能确保安装的是最新版本。

3.2 离线安装芯片包 如果网络环境不好,你也可以去ARM官网或ST官网下载对应的.pack文件,然后双击它,安装程序会自动将其安装到Keil的目录下。

3.3 获取STM32标准外设库或HAL库 芯片包装好后,有了基本的寄存器定义。但我们编程一般不会直接操作寄存器,太繁琐了。我们需要ST官方提供的库。

  • 标准外设库(StdPeriph Lib):ST已经停止更新,但对于学习非常经典。你可以在ST官网搜索“STM32 Standard Peripheral Library”找到并下载。
  • HAL库:这是ST现在主推的库,与STM32CubeMX工具深度集成。我推荐新手从这个入手,因为CubeMX可以图形化配置引脚和时钟,自动生成初始化代码,极大提升效率。HAL库通常通过STM32CubeMX软件来管理和下载,或者从ST官网的对应芯片页面下载“STM32CubeFxx”这样的完整包。

下载好后,把库文件解压到一个固定的位置,比如 C:\STM32_Library。记住这个路径,等下新建工程时需要引用。

4. 创建你的第一个固件工程

环境搭好了,我们来创建一个实实在在的工程。假设我们要控制一个LED灯闪烁,并初始化一个串口,这是后续图像传输的基础。

4.1 新建工程与选择芯片 打开Keil,点击 Project -> New uVision Project。选择一个空文件夹来存放你的工程,给工程起个名字,比如 GLM_OCR_Device

点击保存后,会弹出一个设备选择窗口。在这里展开你安装的芯片系列,精确选择你的芯片型号,比如 STMicroelectronics -> STM32F103 Series -> STM32F103C8。点击OK。

4.2 管理运行时环境(RTE) 接下来会弹出“Manage Run-Time Environment”窗口。这是一个很强大的功能,可以勾选需要的软件组件(如RTOS、中间件)。对于第一个简单工程,我们暂时什么都不选,直接点击“OK”。所需的CMSIS核心组件(Cortex Microcontroller Software Interface Standard)会被自动添加。

4.3 添加源文件与库文件 现在工程树里主要是CMSIS的文件。我们需要手动添加ST的库文件和自己的用户代码。

在项目窗口的“Project”标签页,右键点击“Target 1”,选择“Add Group...”来创建几个文件夹,这样结构更清晰,比如:

  • User:存放自己的主程序(main.c)、中断服务函数等。
  • StdPeriph_DriverHAL_Driver:存放从ST库中提取的驱动源文件。
  • Startup:存放启动文件(通常芯片包已提供,在安装目录的ARM\Startup下可以找到)。

然后,右键点击这些Group,选择“Add Existing Files to Group...”,把对应的.c源文件添加进来。注意,启动文件是.s的汇编文件。

4.4 配置头文件包含路径 编译器需要知道去哪里找头文件。点击魔术棒按钮(Options for Target),在“C/C++”选项卡下,找到“Include Paths”一栏,点击后面的“...”。

添加以下几条路径(根据你的实际存放位置调整):

  • 你的用户代码目录 .\User
  • ST库的包含目录,例如 C:\STM32_Library\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\inc
  • CMSIS核心路径,例如 C:\Keil_v5\ARM\Pack\ARM\CMSIS\5.x.x\CMSIS\Core\Include

4.5 设置宏定义 同样在“C/C++”选项卡,在“Define”框里,根据你使用的库和芯片,添加必要的宏。例如,使用标准外设库且芯片是STM32F103,中等容量,你可能需要添加:USE_STDPERIPH_DRIVER, STM32F10X_MD。这些宏告诉编译器编译哪一部分代码。

5. 编写基础驱动与通信框架

工程框架搭好了,我们来写点最简单的代码,让硬件动起来。这里以实现LED闪烁和串口打印为例。

5.1 GPIO驱动LED闪烁 我们先在main.c里写一个让LED闪烁的程序。假设LED连接在PC13引脚(这是很多迷你开发板的配置)。

#include "stm32f10x.h" // 根据你的芯片头文件包含
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    // 开启GPIOC的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    // 配置PC13为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度50MHz
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    // 初始状态熄灭LED(假设低电平点亮)
    GPIO_SetBits(GPIOC, GPIO_Pin_13);
}

int main(void) {
    // 系统时钟初始化(这里简化,实际项目需仔细配置)
    SystemInit();

    // 初始化LED
    LED_Init();

    while(1) {
        // 点亮LED
        GPIO_ResetBits(GPIOC, GPIO_Pin_13);
        Delay_ms(500); // 需要一个简单的延时函数,下文提供

        // 熄灭LED
        GPIO_SetBits(GPIOC, GPIO_Pin_13);
        Delay_ms(500);
    }
}

// 一个简单的毫秒延时函数(基于SysTick定时器)
void Delay_ms(uint32_t ms) {
    uint32_t i;
    SysTick_Config(SystemCoreClock / 1000); // 配置SysTick为1ms中断一次
    for(i=0; i<ms; i++) {
        // 等待SysTick标志位
        while(!((SysTick->CTRL) & (1<<16)));
    }
    SysTick->CTRL = 0; // 关闭SysTick
}

5.2 串口初始化与通信 接下来初始化一个串口(比如USART1),用于后续与上位机通信。

#include "stm32f10x_usart.h"

void USART1_Init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置PA9为复用推挽输出(TX)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置PA10为浮空输入(RX)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1参数
    USART_InitStructure.USART_BaudRate = baudrate; // 例如 115200
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 发送一个字符
void USART1_SendByte(uint8_t data) {
    USART_SendData(USART1, data);
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

// 发送字符串
void USART1_SendString(char *str) {
    while(*str) {
        USART1_SendByte(*str++);
    }
}

main函数中初始化串口后,你就可以在循环里通过USART1_SendString向上位机发送数据了,比如发送“Hello OCR Host!\r\n”。

6. 编译、下载与调试

代码写好了,得把它变成芯片能执行的二进制文件,并放到芯片里去运行。

6.1 编译工程 点击工具栏上的“Build”按钮(通常是三个箭头组成的图标),或者按F7。下方的“Build Output”窗口会输出编译信息。如果一切顺利,最后会看到 "GLM_OCR_Device.axf" - 0 Error(s), 0 Warning(s)。如果有错误或警告,需要根据提示信息逐条排查,通常是语法错误、路径不对或者宏定义缺失。

6.2 配置调试器 点击魔术棒按钮,进入“Debug”选项卡。这里选择你使用的调试器。如果你用的是ST-Link,在“Use”下拉菜单里可能没有直接选项,需要先安装ST-Link的驱动。安装好后,通常可以选择“CMSIS-DAP Debugger”或“ST-Link Debugger”(如果Keil集成了的话)。更通用的方法是选择“CMSIS-DAP”。

然后点击旁边的“Settings”,在“Debug”子选项卡里确认SW Device扫描到了你的芯片ID。在“Flash Download”子选项卡里,勾选“Reset and Run”,这样下载后程序会自动运行。最重要的是,点击“Add”按钮,为你的具体芯片型号添加正确的Flash编程算法(比如STM32F10x Med-density)。

6.3 下载与调试 用ST-Link连接好开发板,给板上电。点击工具栏的“Load”按钮(一个向下的箭头),程序就会被编译并下载到芯片的Flash中。如果勾选了“Reset and Run”,你会立刻看到LED开始闪烁。

想调试的话,点击“Start/Stop Debug Session”按钮(或Ctrl+F5),进入调试模式。你可以设置断点、单步执行、查看变量和寄存器的值,这对于排查复杂问题非常有用。

7. 总结与后续方向

走完这一遍,一个最基础的嵌入式开发环境就算搭起来了,也完成了点亮LED和串口通信的“Hello World”。这对于控制摄像头、采集图像数据来说,是最底层、也最关键的第一步。GPIO和串口的操作,是后续驱动摄像头模块(通常通过I2C或DCMI接口)和传输图像数据的基石。

实际项目中,你还需要深入几个方面:一是精确配置系统时钟,让芯片跑在设计的频率上;二是学习使用定时器来产生精确的延时或PWM信号(控制摄像头帧率可能用到);三是掌握DMA(直接存储器访问)来高效地搬运图像数据,不占用CPU资源;四是根据你选用的具体摄像头模块(如OV7670、OV2640)编写其寄存器配置代码。

用Keil开发,刚开始配置环境会觉得步骤有点多,但一旦跑通,后面就是不断地在“写代码-编译-下载-调试”这个循环里迭代。遇到问题多查查芯片的数据手册和参考手册,大部分答案都在里面。希望这篇指南能帮你顺利跨出第一步,把硬件控制层的基础打牢。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐