C语言嵌入式系统编程之软件架构篇

嵌入式C项目实战:用Cursor规则搞定代码风格与硬件抽象层(附完整配置流程) 本文详细介绍了如何利用Cursor规则系统规范嵌入式C项目的代码风格与硬件抽象层开发。通过可执行的规范约束,开发者可以确保硬件操作统一通过HAL函数调用,避免直接寄存器访问,并实现中断服务例程的标准化。文章提供了完整的配置流程和实战案例,帮助团队提升代码质量与协作效率。 阅读详情

模块划分

  模块划分的“划”是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了相对论),C语言模块化程序设计需理解如下概念:

  (1) 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明;

  (2) 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明;

  (3) 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

  (4) 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:

  /*module1.h*/

  int a = 5; /* 在模块1的.h文件中定义int a */

  /*module1 .c*/

  #i nclude “module1.h” /* 在模块1中包含模块1的.h文件 */

  /*module2 .c*/

  #i nclude “module1.h” /* 在模块2中包含模块1的.h文件 */

  /*module3 .c*/

  #i nclude “module1.h” /* 在模块3中包含模块1的.h文件 */

  以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这个世界上从来不需要这样的程序。正确的做法是:

  /*module1.h*/

  extern int a; /* 在模块1的.h文件中声明int a */

  /*module1 .c*/

  #i nclude “module1.h” /* 在模块1中包含模块1的.h文件 */

  int a = 5; /* 在模块1的.c文件中定义int a */

  /*module2 .c*/

  #i nclude “module1.h” /* 在模块2中包含模块1的.h文件 */

  /*module3 .c*/

  #i nclude “module1.h” /* 在模块3中包含模块1的.h文件 */

  这样如果模块1、2、3操作a的话,对应的是同一片内存单元。

  一个嵌入式系统通常包括两类模块:

  (1)硬件驱动模块,一种特定硬件对应一个模块;

  (2)软件功能模块,其模块的划分应满足低偶合、高内聚的要求。

  多任务还是单任务

  所谓“单任务系统”是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地“同时”执行多个任务。

  多任务的并发执行通常依赖于一个多任务操作系统(OS),多任务OS的核心是系统调度器,它使用任务控制块(TCB)来管理任务调度功能。TCB包括任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时,要用到这些信息。此外,TCB还被用来存放任务的“上下文”(context)。任务的上下文就是当一个执行中的任务被停止时,所要保存的所有信息。通常,上下文就是计算机当前的状态,也即各个寄存器的内容。当发生任务切换时,当前运行的任务的上下文被存入TCB,并将要被执行的任务的上下文从它的TCB中取出,放入各个寄存器中。

  嵌入式多任务OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遥不可及的神坛之物,我们可以用不到1000行代码实现一个针对80186处理器的功能最简单的OS内核,作者正准备进行此项工作,希望能将心得贡献给大家。

  究竟选择多任务还是单任务方式,依赖于软件的体系是否庞大。例如,绝大多数手机程序都是多任务的,但也有一些小灵通的协议栈是单任务的,没有操作系统,它们的主程序轮流调用各个软件模块的处理程序,模拟多任务环境。

  单任务程序典型架构

  (1)从CPU复位时的指定地址开始执行;

  (2)跳转至汇编代码startup处执行;

  (3)跳转至用户主程序main执行,在main中完成:

  a.初试化各硬件设备;

  b.初始化各软件模块;

  c.进入死循环(无限循环),调用各模块的处理函数

  用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循环,其首选方案是:

  while(1)

  {

  }

  有的程序员这样写:

  for(;;)

  {

  }

  这个语法没有确切表达代码的含义,我们从for(;;)看不出什么,只有弄明白for(;;)在C语言中意味着无条件循环才明白其意。

  下面是几个“着名”的死循环:

  (1)操作系统是死循环;

  (2)WIN32程序是死循环;

  (3)嵌入式系统软件是死循环;

  (4)多线程程序的线程处理函数是死循环。

  你可能会辩驳,大声说:“凡事都不是绝对的,2、3、4都可以不是死循环”。Yes,you are right,但是你得不到鲜花和掌声。实际上,这是一个没有太大意义的牛角尖,因为这个世界从来不需要一个处理完几个消息就喊着要OS杀死它的WIN32程序,不需要一个刚开始RUN就自行了断的嵌入式系统,不需要莫名其妙启动一个做一点事就干掉自己的线程。有时候,过于严谨制造的不是便利而是麻烦。君不见,五层的TCP/IP协议栈超越严谨的ISO/OSI什么是OSI?

  OSI是一个开放性的通行系统互连参考模型,他是一个定义的非常好的协议规范。OSI模型有7层结构,每层都可以有几个子层。

  七层协议栈大行其道成为事实上的标准?

  经常有网友讨论:

  printf(“%d,%d”,++i,i++); /* 输出是什么?*/

  c = a+++b; /* c=? */

  等类似问题。面对这些问题,我们只能发出由衷的感慨:世界上还有很多有意义的事情等着我们去消化摄入的食物。

  实际上,嵌入式系统要运行到世界末日。

  中断服务程序

  中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序(ISR),类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。

  中断服务程序需要满足如下要求:

  (1)不能返回值;

  (2)不能向ISR传递参数;

  (3) ISR应该尽可能的短小精悍;

  (4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。

  在某项目的开发中,我们设计了一个队列,在中断服务程序中,只是将中断类型添加入该队列中,在主程序的死循环中不断扫描中断队列是否有中断,有则取出队列中的第一个中断类型,进行相应处理。

  /* 存放中断的队列 */

  typedef struct tagIntQueue

  {

  int intType; /* 中断类型 */

  struct tagIntQueue *next;

  }IntQueue;

  IntQueue lpIntQueueHead;

  __interrupt ISRexample ()

  {

  int intType;

  intType = GetSystemType();

  QueueAddTail(lpIntQueueHead, intType);/* 在队列尾加入新的中断 */

  }

  在主程序循环中判断是否有中断:

  While(1)

  {

  If( !IsIntQueueEmpty() )

  {

  intType = GetFirstInt();

  switch(intType) /* 是不是很象WIN32程序的消息解析函数? */

  {

  /* 对,我们的中断类型解析很类似于消息驱动 */

  case xxx: /* 我们称其为“中断驱动”吧? */

  …

  break;

  case xxx:

  …

  break;

  …

  }

  }

  }

  按上述方法设计的中断服务程序很小,实际的工作都交由主程序执行了。

  硬件驱动模块

  一个硬件驱动模块通常应包括如下函数:

  (1)中断服务程序ISR

  (2)硬件初始化

  a.修改寄存器,设置硬件参数(如UART应设置其波特率,AD/DA设备应设置其采样速率等);

  b.将中断服务程序入口地址写入中断向量表:

  /* 设置中断向量表 */

  m_myPtr = make_far_pointer(0l); /* 返回void far型指针void far * */

  m_myPtr += ITYPE_UART; /* ITYPE_UART: uart中断服务程序 */

  /* 相对于中断向量表首地址的偏移 */

  *m_myPtr = &UART _Isr; /* UART _Isr:UART的中断服务程序 */

  (3)设置CPU针对该硬件的控制线

  a.如果控制线可作PIO(可编程I/O)和控制信号用,则设置CPU内部对应寄存器使其作为控制信号;

  b.设置CPU内部的针对该设备的中断屏蔽位,设置中断方式(电平触发还是边缘触发)。

  (4)提供一系列针对该设备的操作接口函数。例如,对于LCD,其驱动模块应提供绘制像素、画线、绘制矩阵、显示字符点阵等函数;而对于实时钟,其驱动模块则需提供获取时间、设置时间等函数。

  C的面向对象化

  在面向对象的语言里面,出现了类的概念。类是对特定数据的特定操作的集合体。类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合,我们可以利用函数指针将struct模拟为一个包含数据和操作的“类”。下面的C程序模拟了一个最简单的“类”:

  #ifndef C_Class

  #define C_Class struct

  #endif

  C_Class A

  {

  C_Class A *A_this; /* this指针 */

  void (*Foo)(C_Class A *A_this); /* 行为:函数指针 */

  int a; /* 数据 */

  int b;

  };

  我们可以利用C语言模拟出面向对象的三个特性:封装、继承和多态,但是更多的时候,我们只是需要将数据与行为封装以解决软件结构混乱的问题。C模拟面向对象思想的目的不在于模拟行为本身,而在于解决某些情况下使用C语言编程时程序整体框架结构分散、数据和函数脱节的问题。我们在后续章节会看到这样的例子。

  总结

  本篇介绍了嵌入式系统编程软件架构方面的知识,主要包括模块划分、多任务还是单任务选取、单任务程序典型架构、中断服务程序、硬件驱动模块设计等,从宏观上给出了一个嵌入式系统软件所包含的主要元素。

  请记住:软件结构是软件的灵魂!结构混乱的程序面目可憎,调试、测试、维护、升级都极度困难。

win10+conda+pytorch-gpu安装 pytorch 官网上只有linux和Mac的程序包,没有windows系统的,但是windows系统还是可以用pytorch的。  github: https://github.com/peterjc123/pytorch-scripts 首先要安装anaconda: 推荐清华镜像 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ ... 阅读详情

相关推荐

Python与西门子PLC数据交互实战:基于Snap7的跨平台工业通信指南

本文详细介绍了如何使用Python和Snap7库实现与西门子PLC的数据交互。内容涵盖跨平台环境搭建、连接参数配置、数据读写实战及性能优化技巧,为工业自动化、数据采集和智能制造场景提供了一套轻量、灵活且高效的通信解决方案。

vim8coder的博客 276

嵌入式开发神器:用 Cursor 轻松搞定代码注释与优化!

嵌入式开发中,代码注释和逻辑优化是提升代码质量的关键。Cursor 作为一款 AI 驱动的代码编辑器,能够帮助你轻松搞定这些难题。,它基于 AI 技术,能够自动生成代码注释、优化代码逻辑,甚至帮你解释复杂代码!嵌入式开发中,代码注释是提升代码可读性和可维护性的关键。输入提示词:“生成一个函数,用于控制LED的亮度,使用PWM调光”,Cursor 会生成如下代码:​​​​​​​。是一款智能代码编辑器,内置强大的 AI 功能,专为开发者设计。:用简单的语言描述你的需求,Cursor 会自动生成代码或注释。

zuenquan521的博客 3021

【Android】条形码/二维码扫描——ZXing源码分析

正在更新中 针对3.2.1版本源码 与其他版本代码可能会有出入,但核心思想基本不变。 首先从com.google.zxing.client.android包中的目录结构分析:Book:这个文件夹中有5个类: BrowseBookListener 实现了adapter的点击事项响应,通过获取ISBN,然后跳转到Google books中查询响应的详细信息。 SearchBookContents

年轻的学徒 2446

Cursor 上手

它可以帮助你计算整个代码库的嵌入式信息,使得在询问代码相关问题时,你能得到更加智能的回答。的扩展、设置和快捷键绑定只是将配置和相关数据迁移到新的编辑器环境(如 Cursor),而不会重复下载已经安装的扩展。允许你选择自己熟悉的快捷键方式,例如 Vim 或 Emacs,让你的编辑体验更加顺畅。这些设置不仅提高了开发效率,还为你提供了更多自定义选项,帮助你打造适合个人需求的编程环境。可以节省时间,让你直接从终端中进行操作,无需离开当前的命令行环境。具体来说,安装这个命令后,你将能够在终端中通过输入。

m0_74331272的博客 1121

电涡流位移测量技术深度解密

电涡流位移传感器凭借非接触测量、高精度(可达纳米级)、强环境适应性(耐高温高压油污)等优势,成为工业精密位移检测的核心技术。其工作原理基于电磁感应:高频磁场在金属靶面感应涡流,通过检测线圈阻抗变化转化为位移信号。应用需注意靶材依赖性(需按实际材料标定)、安装精度及抗干扰措施。广泛应用于磁悬浮轴承、航空发动机、精密制造等领域,实现对微米级位移的可靠监测。

技术工作者 1738

EWF写保护功能详解:从安装到实战操作指南

本文详细介绍了EWF写保护功能的安装与实战操作指南。EWF(Enhanced Write Filter)通过重定向写入操作保护硬盘分区,特别适合需要长期稳定运行的系统场景。文章涵盖从安装准备、图形界面操作到命令行高级管理的全流程,并提供了常见问题的解决方案,帮助用户轻松掌握这一实用工具。

weixin_28710999的博客 438

掌握这些Cursor技巧,嵌入式开发效率翻倍!(超详细版)

Cursor内置丰富的嵌入式开发代码库,涵盖常用外设驱动、通信协议、算法等,方便你快速查找和使用。除了基本的代码补全,Cursor还能根据上下文推测你的意图,提供更精准的补全建议。Cursor官网提供了丰富的学习资源,例如教程、文档、视频等,方便你学习和使用Cursor。Cursor能够实时分析你的代码,并标记出潜在的语法错误和逻辑错误,帮助你快速定位问题。可以自定义注释模板,例如函数注释模板、文件注释模板等,方便快速生成规范的注释。快捷键可以快速搜索代码,支持正则表达式搜索,方便你快速定位代码位置。

zuenquan521的博客 2329

STM32F103模拟SSI时序解析:实现高精度绝对式编码器位置读取

本文详细解析了如何在STM32F103微控制器上通过软件模拟SSI协议时序,实现高精度绝对式编码器的位置读取。文章从项目驱动出发,深入剖析了SSI通信原理、硬件电平转换连接、核心软件模拟代码实现及关键时序参数调试方法,并提供了使用示波器进行波形对齐与优化的实战指南,为成本敏感型工业控制与机器人项目提供了高性价比的解决方案。

weixin_29196315的博客 138

嵌入式编程软件基础汇总1

嵌入式编程1,模块划分2,任务模式2.1,单任务3,中断服务程序4, 工作知识学习及总结系列文档 本文主要记录学习嵌入式软硬件编程过程中的一些基础知识 1,模块划分 模块划分的“划”是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分,合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能。 模块划分的方法 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明; 某模块提供给其它模块调用的外部函数及数据需在.h文件中冠以exter

ALISAlwz的博客 3852

QGroundControl高级配置与自定义功能实战指南

本文深入探讨了QGroundControl(QGC)的高级配置与自定义功能,为无人机开发者、系统集成商和高级用户提供实战指南。内容涵盖从品牌界面定制、自定义MAVLink消息处理,到插件开发与自动化部署的全流程,旨在帮助用户解锁QGC的隐藏潜力,打造专属的行业解决方案。

weixin_29234509的博客 235

无人驾驶汽车系统入门(三十二)——FEC算法:点云快速欧几里得聚类实战解析

本文深入解析了FEC算法在无人驾驶点云聚类中的高效应用,通过逐点处理策略和体素栅格预分割技术,实现了比传统欧几里得聚类快百倍的速度。文章详细介绍了算法原理、Python实现及参数调优指南,并对比了FEC与传统算法的性能差异,为无人驾驶系统开发提供了实用解决方案。

weixin_29065015的博客 479

C语言嵌入式系统编程修炼之软件架构篇

这是我13年前创作和发表在互联网上的文章,这么多年过去了,这篇文章仍然在到处传播。现在贴回Linuxer公众号。全文目录:C语言嵌入式系统编程修炼之道——背景篇C语言嵌入式系统编程修炼之道——软件架构篇1.模块划分2.多任务还是单任务3.单任务程序典型架构4.中断服务程序5.硬件驱动模块6.C的面向对象化总结C语言嵌入式系统编程修炼之道——内存操作篇1.数据指针2.函数指针3.数组vs.动态申请4

Linux阅码场 6743

使用 Simulink/Simscape Electrical/Stateflow,构建一个并网逆变器短路故障动态响应与恢复策略仿真模型

本文基于Simulink平台,详细介绍了并网逆变器短路故障的动态响应与恢复策略仿真方法。主要内容包括:1)构建包含热模型的高精度逆变器模型;2)模拟IGBT击穿和桥臂直通两种短路故障;3)设计基于Stateflow的微秒级快速保护逻辑;4)实现放电、去磁、自检、重启的完整恢复流程。仿真结果表明,所提出的保护方案能在200μs内有效限制故障电流,避免器件永久损坏,并通过标准化恢复流程实现系统安全重启。文章还探讨了硬件保护协同、多级保护和预测保护等高级策略,为电力电子系统安全防护提供了实用解决方案。

xiaoheshang_123的博客 103

C语言嵌入式系统编程修炼之二:软件架构篇

C语言嵌入式系统编程修炼之二:软件架构篇 转自: 2007年08月21日 天极网 字号: 大 中 小   模块划分   模块划分的"划"是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了相对论),C语言模块化程

Cs121 1128

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C语言嵌入式系统编程修炼之道——软件架构篇

  C语言嵌入式系统编程修炼之道——软件架构篇1.模块划分模块划分的“划”是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了相对论),C语言模块化程序设计需理解如下概念:(1)   模块即是一个.c文件和一个.h文件的结合

天高路远 838

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C语言嵌入式系统编程修炼之软件架构篇 2

硬件驱动模块   一个硬件驱动模块通常应包括如下函数:   (1)中断服务程序ISR   (2)硬件初始化   a.修改寄存器,设置硬件参数(如UART应设置其波特率,AD/DA设备应设置其采样速率等);   b.将中断服务程序入口地址写入中断向量表

工匠 885

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

c语言 工程 系统 架构,C语言嵌入式系统编程之软件架构篇

中断服务程序中断是嵌入式系统中重要的组成部分,但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持,提供新的关键字用于标示中断服务程序(ISR),类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候,编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。中断服务程序需要满足如下要求:(1)不能返回值;(2)不能向ISR传...

weixin_32503361的博客 612

【转载】C语言嵌入式系统编程修炼之二:软件架构篇

C语言嵌入式系统编程之软件架构篇 模块划分   模块划分的“划”是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设 计语言,在模块的划分上主要依据功能(依功能进行划分在面向对象设计中成为一个错误,牛顿定律遇到了相对论),C语言模块化程序设计需理解如下概念:   (1) 模块即是一个.c文件和一个.h文件的结...

weixin_30727835的博客 366
上一篇: 详解大端模式和小端模式
下一篇: 杨毅:不够优秀就不要腆着脸继续占便宜
求佛_ce123
求佛_ce123 领域专家: 嵌入式与硬件开发技术领域 领域专家: 嵌入式与硬件开发技术领域
博客等级 码龄16年 4185粉丝 402原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值