[MDK] 介绍STM32使用C和C++混合编程的方法

目录

  • [MDK] 介绍STM32使用C和C++混合编程的方法
    • 前言
    • 业务场景
    • 步骤1基础工程
    • 步骤2写代码
    • 步骤3添加cpp文件
    • 步骤4配置与编译
    • 上机现象
    • 后记

[MDK] 介绍STM32使用C和C++混合编程的方法

前言

搞单片机编程大多数还是使用MDK编程,自己对MDK这个软件也比较熟悉,在网络寻找资料时,发现有一些大佬会用c++来写单片机程序,很是高大上,于是笔者也想研究一下,于是有了这篇文章,使用stm32的内部flash进行编程,在芯片内部flash的最后一个page上进行存储一些数据。

业务场景

假设公司有一个项目是专门做智能家居的主板,这些主板上智能的功能比较多,可以语音控制某个灯,智能控制某个场景,开光空调等一系列的高级功能,现在这些主板用来供给酒店客房,酒店的这些客房都是装同一套板子,但是每个客房中一些配置又有一些不同,比如客房A比较大装有18个灯,15个按键;客房B比较小,只装有5个灯,3个按键。现在的需求则是根据客户每个房间的配置来对智能主板上进行部分编程。

步骤1基础工程

找一个基础工程,使用cubemx生成一个最基本的项目,时钟和SWD配置好就行,可以参考hal库教程。

步骤2写代码

移植现有代码

在这里插入图片描述

random_flash_interface.h内容

#ifndef FlashStorage_STM32_h
#define FlashStorage_STM32_h

#include "random_flash_utils.h"


class EEPROM
{
public:
    EEPROM()
    = default;


    uint8_t Read(int _address)
    {
        if (!isInitialized)
            init();

        return EEPROMReadBufferedByte(_address);
    }


    void Update(int _address, uint8_t _value)
    {
        if (!isInitialized)
            init();

        if (EEPROMReadBufferedByte(_address) != _value)
        {
            dirtyBuffer = true;
            EEPROMWriteBufferedByte(_address, _value);
        }
    }


    void Write(int _address, uint8_t _value)
    {
        Update(_address, _value);
    }


    template<typename T>
    T &Pull(int _offset, T &_t)
    {
        // Copy the data from the flash to the buffer if not yet
        if (!isInitialized)
            init();

        uint16_t offset = _offset;
        auto* _pointer = (uint8_t*) &_t;

        for (uint16_t count = sizeof(T); count; --count, ++offset)
        {
            *_pointer++ = EEPROMReadBufferedByte(offset);
        }

        return _t;
    }


    template<typename T>
    const T &Push(int _idx, const T &_t)
    {
        // Copy the data from the flash to the buffer if not yet
        if (!isInitialized) init();

        uint16_t offset = _idx;

        const auto* _pointer = (const uint8_t*) &_t;

        for (uint16_t count = sizeof(T); count; --count, ++offset)
        {
            EEPROMWriteBufferedByte(offset, *_pointer++);
        }

        if (commitASAP)
        {
            // Save the data from the buffer to the flash right away
            EEPROMBufferFlush();

            dirtyBuffer = false;
            isValid = true;
        } else
        {
            // Delay saving the data from the buffer to the flash. Just flag and wait for commit() later
            dirtyBuffer = true;
        }

        return _t;
    }


    void Commit()
    {
        if (!isInitialized)
            init();

        if (dirtyBuffer)
        {
            // Save the data from the buffer to the flash
            EEPROMBufferFlush();

            dirtyBuffer = false;
            isValid = true;
        }
    }


    static uint16_t TotalSize()
    {
        return EEPROM_SIZE + 1;
    }


    void SetCommitASAP(bool value = true)
    {
        commitASAP = value;
    }


    bool isValid = true;


private:
    void init()
    {
        // Copy the data from the flash to the buffer
        EEPROMFillBuffer();
        isInitialized = true;
    }


    bool isInitialized = false;
    bool dirtyBuffer = false;
    bool commitASAP = true;
};


#endif

random_flash_utils.cpp内容

#ifndef __STM32_EEPROM_HPP
#define __STM32_EEPROM_HPP

#ifdef __cplusplus
extern "C" {
#endif

#include <cstring>
//#include <string.h>
#include "random_flash_utils.h"


#define FLASH_BANK_NUMBER       FLASH_BANK_1
#define FLASH_END               FLASH_BANK1_END
#define FLASH_BASE_ADDRESS      ((uint32_t)((FLASH_END + 1) - EEPROM_SIZE))


static uint8_t eepromBuffer[EEPROM_SIZE] __attribute__((aligned(8))) = {0};


static inline uint32_t GetFlashEndAddress()
{
    uint32_t size;
    switch ((*((uint16_t*) FLASH_SIZE_DATA_REGISTER)))
    {
        case 0x200U:
            size = 0x0807FFFFU;
            break;
        case 0x100U:
            size = 0x0803FFFFU;
            break;
        case 0x80U:
            size = 0x0801FFFFU;
            break;
        case 0x40U:
            size = 0x0800FFFFU;
            break;
        case 0x20U:
            size = 0x08007FFFU;
            break;
        default:
            size = 0x08003FFFU;
            break;
    }
    return size;
}


uint8_t EEPROMReadByte(const uint32_t _pos)
{
    EEPROMFillBuffer();
    return EEPROMReadBufferedByte(_pos);
}


void EEPROMWriteByte(uint32_t _pos, uint8_t _value)
{
    EEPROMWriteBufferedByte(_pos, _value);
    EEPROMBufferFlush();
}


uint8_t EEPROMReadBufferedByte(const uint32_t _pos)
{
    return eepromBuffer[_pos];
}


void EEPROMWriteBufferedByte(uint32_t _pos, uint8_t _value)
{
    eepromBuffer[_pos] = _value;
}


void EEPROMFillBuffer(void)
{
    memcpy(eepromBuffer, (uint8_t*) (FLASH_BASE_ADDRESS), EEPROM_SIZE);
}


void EEPROMBufferFlush(void)
{
    FLASH_EraseInitTypeDef eraseInitStruct;
    uint32_t offset = 0;
    uint32_t address = FLASH_BASE_ADDRESS;
    uint32_t address_end = FLASH_BASE_ADDRESS + EEPROM_SIZE - 1;
    uint32_t pageError = 0;
    uint64_t data = 0;

    /* ERASING page */
    eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    eraseInitStruct.Banks = FLASH_BANK_NUMBER;
    eraseInitStruct.PageAddress = FLASH_BASE_ADDRESS;
    eraseInitStruct.NbPages = 1;

    if (HAL_FLASH_Unlock() == HAL_OK)
    {
        __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR);

        if (HAL_FLASHEx_Erase(&eraseInitStruct, &pageError) == HAL_OK)
        {
            while (address <= address_end)
            {
                data = *((uint64_t*) ((uint8_t*) eepromBuffer + offset));

                if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data) == HAL_OK)
                {
                    address += 8;
                    offset += 8;
                } else
                    address = address_end + 1;
            }
        }
        HAL_FLASH_Lock();
    }
}


#ifdef __cplusplus
}
#endif
#endif


random_flash_utils.h内容

#ifndef __STM32_EEPROM_H
#define __STM32_EEPROM_H

#ifdef __cplusplus
extern "C" {
#include <stm32f103xb.h>
#include <stm32f1xx_hal.h>
#endif

#define EEPROM_SIZE  FLASH_PAGE_SIZE // 1K Byte


uint8_t EEPROMReadByte(uint32_t _pos);
void EEPROMWriteByte(uint32_t _pos, uint8_t _value);

void EEPROMFillBuffer();
void EEPROMBufferFlush();
uint8_t EEPROMReadBufferedByte(uint32_t _pos);
void EEPROMWriteBufferedByte(uint32_t _pos, uint8_t _value);


#ifdef __cplusplus
}
#endif
#endif

讲上述的源码添加进工程中,并配置头文件路径

在这里插入图片描述

步骤3添加cpp文件

在main.c中添加一个C函数,用于C++代码的入口

#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "common_inc.h"

int main(void)
{
  /* USER CODE BEGIN 1 */
  
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  
   printf("%d\r\n",SystemCoreClock);
   Main();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}

这个Main函数在一个头文件中进行声明,在main_app.cpp中进行实现

common_inc.h的内容

#ifndef LOOP_H
#define LOOP_H

#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------- C Scope ---------------------------*/
//#include "stdint-gcc.h"
#include "stm32f1xx_hal.h"
#include "main.h"
//#include "tim.h"
//#include "usbd_customhid.h"
//#include "usb_device.h"


void Main();


#ifdef __cplusplus
}


/*---------------------------- C++ Scope ---------------------------*/
#include "random_flash_interface.h"


#endif
#endif

main_app.cpp的内容

#include "common_inc.h"
#include "configurations.h"
#include <cstring>

/* Component Definitions -----------------------------------------------------*/
RoomConfig_t roomConfigs;

/* Main Entry ----------------------------------------------------------------*/
void Main()
{
	  EEPROM eeprom;
	  eeprom.Pull(0, roomConfigs);
	  if (roomConfigs.configStatus != CONFIG_OK)
    {
        // Use default settings
        roomConfigs = RoomConfig_t{
            .configStatus = CONFIG_OK,
            .roomType = 1, // 默认客房类型
            .numLamps = 2, // 默认灯数量
            .numButtons = 3, // 默认按键数量
            .lampTypes = {LAMP_NIGHT, LAMP_READ},   // 灯带,玄武灯,床头灯,顶灯,射灯...
            .buttonFunctions = {BUTTON_ALL_LIGHTS,BUTTON_SLEEP,BUTTON_WAKEUP} // 情景模式,全开灯,全关灯...
        };
				
        eeprom.Push(0, roomConfigs);
    }
    // 主循环,这里可以添加实际的功能代码
    while (true)
    {
        // 根据客房配置处理智能客房的逻辑
        // 例如,根据roomConfigs[roomId].lampTypes和roomConfigs[roomId].buttonFunctions来控制灯和按键
    }
}

还有一个配置的头文件configurations.h

#ifndef CONFIGURATIONS_H
#define CONFIGURATIONS_H

#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------- C Scope ---------------------------*/
#include <stdbool.h>
#include "common_inc.h"


typedef enum configStatus_t
{
    CONFIG_RESTORE = 0,
    CONFIG_OK,
    CONFIG_COMMIT
} configStatus_t;

typedef enum lampType_t
{
    LAMP_NIGHT, // 夜灯
    LAMP_READ   // 阅读灯
	  
} lampType_t;

typedef enum buttonFunction_t
{
    BUTTON_ALL_LIGHTS, // 全开灯
	  BUTTON_SLEEP,
	  BUTTON_WAKEUP
} buttonFunction_t;

typedef struct RoomConfig_t
{
    configStatus_t configStatus;
    uint8_t roomType;
    uint8_t numLamps;
    uint8_t numButtons;
    lampType_t lampTypes[8];        // 假设最多8个灯
    buttonFunction_t buttonFunctions[8];  // 假设最多8个按键
} RoomConfig_t;


extern RoomConfig_t roomConfigs;


#ifdef __cplusplus
}
/*---------------------------- C++ Scope ---------------------------*/



#endif
#endif

步骤4配置与编译

使用vscode编写完代码后,配置一下keil工程用来编译,我的配置如下

在这里插入图片描述

在这里插入图片描述

设置好之后,编译一下工程,然后进行上机实验。

上机现象

在这里插入图片描述

在这里插入图片描述

上机之后,使用调试功能进行仿真一下,发现在对应的地址上存储了自己的配置信息,跟定义的配置是一致的。

后记

我的这颗ic,最后一个page的 是从0x0801FC00开始的,所以我仿真时在内存窗口查看了这个地址。

在这里插入图片描述

使用c++来写单片机编程的是有,不过都是商业案例,也不太好找到相关的代码片段,何况搞单片机的主流还是c,没啥人愿意折腾新东西,自己是比较能接收新鲜事物的,所以做了这篇的实战记录。

本文记录到此,算是自己工程的一次实践,本文完!!感谢阅读,感谢关注。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【RAG 论文】Dense X 检索:将“命题”作为检索粒度

论文&#xff1a;Dense X Retrieval: What Retrieval Granularity Should We Use? ⭐⭐⭐⭐ Code: github.com/ct123098/factoid-wiki 文章目录 一、论文速读二、命题&#xff08;Proposition&#xff09;三、FactoidWiki四、实验及分析4.1 Passage Retrieval 任务4.2 Open-Do…

前端css中径向渐变(radial-gradient)的使用

前端css中径向渐变的使用 一、前言二、主要内容说明&#xff08;一&#xff09;、径向渐变的形状1.椭圆形渐变&#xff08;ellipse&#xff09;&#xff0c;源码12.源码1运行效果3.圆形渐变&#xff08;circle&#xff09;&#xff0c;源码24.源码2运行效果 &#xff08;二&…

遇到螺纹连接过程中的软连接,怎么办?——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-扭矩自动控制系统-SunTorque 在螺纹连接过程中遇到软连接时&#xff0c;首先需要明确软连接的概念及其特点。软连接通常指的是在螺栓拧紧过程中&#xff0c;由于紧固件与被连接件之间的材料、表面状况或装配工艺等因素&#xff0c;导致拧紧力矩不能…

分布式关系型数据库管理系统 OceanBase 安装和配置教程

&#x1f30a; 分布式关系型数据库管理系统 OceanBase 安装和配置教程 &#x1f680; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝…

Kubernetes核心概念基本操作

1.1 Namespace命名空间 1.1.1 Namespace核心概念 Kubernetes 的 Namespace&#xff08;命名空间&#xff09;是一种用于创建逻辑隔离分区的机制&#xff0c;它的主要作用是用来实现多套环境的资源隔&#xff0c;它允许用户在同一个物理集群中模拟出多个虚拟集群的效果。以下是…

APP广告变现:自刷的秘密与规则

在移动互联网时代&#xff0c;广告已成为众多APP盈利的主要方式之一。对于开发者和运营者而言&#xff0c;如何通过广告变现提高收益是他们必须关注的问题。然而&#xff0c;在众多的变现方法中&#xff0c;“自刷广告”这一概念可能让一些人感到迷惑。实际上&#xff0c;只要在…

在Mars3d实现cesium的ImageryLayer自定义瓦片的层级与原点

需要自定义瓦片层级和原点&#xff0c;所以需要自己写第三方图层&#xff0c;但是之前写的很多方法&#xff0c;图层控制和显隐以及透明度&#xff0c;需要跟之前的交互一直&#xff0c;改动量太大的话不划算&#xff0c;所以直接看Mars3d的layer基类&#xff0c;把重写的image…

python词云图背景颜色修改

python词云图背景颜色修改 词云图介绍wordcloud介绍修改背景颜色 词云图介绍 词云图&#xff08;Word Cloud&#xff09;是一种文本数据的可视化表示形式&#xff0c;它通过字体大小、颜色、布局等视觉元素来展示文本中不同词汇的频率或重要性。词云图中&#xff0c;出现频率高…

怎么清理服务器的C盘?

有时候我们经常会遇到C盘被占满的情况&#xff0c;C盘被占满的原因有很多&#xff0c;下面我们就来分析下有可能导致C盘占满的原因&#xff1a; 第一种情况&#xff1a;中毒 打开服务器任务管理器选择进程&#xff0c;并且勾选显示所有用户的进程&#xff0c;我们可以点击映像…

PHPStudy 下载PHP提示“当前网络不稳定,下载失败”

错误信息 当前网络不稳定&#xff0c;下载失败 获取下载链接失败&#xff0c;请检查网络 假查网络 问题原因 xp.cn服务器的网络不稳定&#xff0c;不是你电脑的网络问题。 解决办法 第一步&#xff1a;下载现成的PHP文件 直接下载现成的文件&#xff0c;放到php目录。 将…

FlashAttention(flash-attn)安装

FlashAttention&#xff08;flash-attn&#xff09;安装 Flash Attention是一种注意力算法&#xff0c;用于减少这一问题&#xff0c;并更有效地缩放基于转换器的模型&#xff0c;从而实现更快的训练和推理。标准注意力机制使用高带宽内存&#xff08;HBM&#xff09;来存储、…

写一个函数返回参数二进制中1的个数--四种方法及原理解释

虽然本方法是java写的&#xff0c;但是其原理适用于大部分语言 方法一&#xff1a;通过取模%运算取出每一位比特位数值&#xff0c;再进行判断&#xff08;该方法不可判断负数&#xff09; 原理&#xff1a; 通过取模num % 2 1 取出该数的每一个二进制位数&#xff0c;再判…

Python selenium

1.搭建环境 1.安装&#xff1a; pip install msedge-selenium-tools 不要使用pip install selenium&#xff0c;我的电脑上没法运行 2.下载驱动 Microsoft Edge WebDriver |Microsoft Edge 开发人员 edge浏览器点设置---关于即可找到版本号&#xff0c;一定要下载对应版…

不止是搭建 | 极空间虚拟机安装一个可做生产力的Ubuntu桌面系统以及后续优化

不止是搭建 | 极空间虚拟机安装一个可做生产力的Ubuntu桌面系统以及后续优化 哈喽小伙伴们好&#xff0c;偶是Stark-C~ 我在上篇极空间文章中不是给小伙伴们分享了使用虚拟机安装软路由固件『iStoreOS』的教程嘛&#xff1a; 打造Docker完全体&#xff0c; 开箱即用的各类插…

Windows Server 2019虚拟机安装

目录 第一步、准备工作 第二步、部署虚拟机 第三步、 Windows Server 2019系统启动配置 第一步、准备工作 下载Windows Server 2019系统镜像 官网下载地址&#xff1a;Windows Server 2019 | Microsoft Evaluation Center VMware Workstation 17下载地址&#xff1a; 链…

各行业预约上门服务小程序源码系统 在线提交表单+自主接单 带完整的安装代码包以及搭建教程

在当今数字化快速发展的时代&#xff0c;传统行业纷纷寻求与互联网的结合&#xff0c;以提升服务效率和用户体验。为了满足这一需求&#xff0c;罗峰给大家分享一款针对各行业预约上门服务的小程序源码系统。该系统集在线提交表单、自主接单等功能于一体&#xff0c;并附带完整…

C语言贪吃蛇

注 &#xff1a;本文是基于链表实现贪吃蛇游戏 1.Win32 API 本篇文章中实现贪吃蛇会用到一些Win32 API的知识&#xff0c;接下来简单做下介绍 1.1 Win32 API Windows 这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外&#xff0c; 它同时也是⼀个 很大的服务中…

熟悉Redis吗,那Redis的过期键删除策略是什么

对于Redis&#xff0c;我们业务开发一般都只关心Redis键值对的查询、修改操作&#xff0c;可能因为懒或者只想能用就行&#xff0c;呵呵。很少关心键值对存储在什么地方、键值对过期了会怎么样、Redis有没什么策略处理过期的键、Redis处理过期键又有什么作用&#xff1f;但这些…

《深入Linux内核架构》第4章 进程虚拟内存(1)

目录 4.1 简介 4.2 进程虚拟地址空间 4.2.1 进程地址空间分布 4.2.2 建立布局 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;订阅后续文章。 第3章讲了两点&#xff1a;物理内存的管理&#xff0c;内核虚拟地址管理。 本章讲&#xff1a;用户进程的虚拟地址空间…

黄金投资怎么判断走势好坏?

投资黄金&#xff0c;就是押注于这一贵金属价格的变动。判断黄金价格的走势好坏&#xff0c;对于投资者来说至关重要。这需要从宏观经济指标、技术分析指标和市场情绪等多元化角度进行综合分析。 宏观经济指标 货币政策&#xff1a;中央银行的货币政策&#xff0c;尤其是利率决…