对AVR芯片进行编程时如何定义内存指针?

如何解决对AVR芯片进行编程时如何定义内存指针?

序言:在作为应用程序开发人员工作了几年之后,软件工程的世界变得比以往更加晦涩难懂。原因是真正的东西隐藏在成千上万的抽象层之下:操作系统,框架等。年轻一代被剥夺了使用类似PDP的机器的乐趣,在这些机器上所有编程都是通过电开关切换完成的。另一个问题是现代编程语言的短暂性质。曾经有Python 2.x,现在已不推荐使用,而Python 3.x则将在几个月后不推荐使用。同上其他语言。 ANSI C看起来像是Cheops的金字塔:它在70年代就存在了,我毫不怀疑它会在太阳变成红矮星之后出现。

现在看来,了解硬件和软件之间相互作用的唯一方法是玩嵌入式开发。从教学的角度来看,物理芯片非常方便,因为它们可以解决C语言最困难的部分,即指针。在OS环境中进行编码时,* /&表示法仍然很令人困惑,因为它指向虚拟内存内部的某个位置。并且在您了解什么是虚拟内存之前,您必须阅读关于操作系统开发等的一些专着。您可能会觉得它很愚蠢,但是我确实很想知道现在哪个晶体管在阻碍我的发展。至少,我可以将物理引脚电压连接到编程抽象。

由于许多教科书和可访问的硬件,目前我正在使用Atmel芯片和WinAVR软件包。尽管所有书籍都承诺使用纯C语言教授AVR编码,但实际情况是所有指针都隐藏在PORTA,DDRB等宏后面。所有代码示例均包括头文件io.h,该文件又引用了其他特定的头文件给定的芯片,例如“ iomx8.h”。到目前为止,我在这些标头中找不到任何宏定义。在Atmega168上增加物理引脚14上的电压的代码看起来像

DDRB = 0x01;
PORTB = 0x01;

幸运的是,Microchip网站提供了一些基本文档,例如,如果要提高物理引脚14的电压,则需要执行以下步骤:

unsigned char *ddrB;
ddrB = (unsigned char*)0x24; // the address of ddrB is 0x24
*ddrB |= 0x01; // set up low impedance/ high current state for the transistor 0 

unsigned char *portB;
portB = (unsigned char*)0x25;
*portB |= 0x01; // voltage on
*portB &= ~(0x01); // voltage off

不幸的是,这是我潜伏一周后获得的唯一信息。现在,我正在经历USART编程,而所有这些UBRR0H,UCSR0C的事情都变得更加复杂。由于提供的头文件不包含任何寄存器的宏定义,因此我还能在哪里找到它?

几年前,有人问过类似的问题:accessing AVR registers with C?。但是,除了GCC本身可以将一些神话般的PORTB映射到实际物理位置的线索外,所提供的答案多少没有用。有人可以描述映射背后的机制吗?

解决方法

从内存映射的角度来看:通用寄存器,特殊功能+ I / O寄存器和SRAM共享非重叠范围的单个地址空间,如数据表中针对不同处理器的描述AVR系列。您的所有指针都将引用此内存空间,除非注释为指向PROGMEM的指针(这将导致发出不同的指令)。无需任何虚拟内存映射即可进行引用。

例如,ATtiny 25/45/85的第18页上显示以下地图:

enter image description here

您的链接器知道此内存映射,并将相应地放置变量。例如,在上述示例设备中,在您的一个编译单元中声明的全局变量将以高于0x0060的地址结束,从而最终在SRAM中结束。

从指令编码的角度来看::尽管只有一个地址空间,但为某些重要区域保留了特殊功能。例如,IN和OUT指令的指令编码中有6位,可用于直接引用[0x20,0x5F)中64个地址之一。

IN和OUT指令具有独特的加载和存储到直接在指令中编码的固定地址的能力,因为正常的加载和存储指令需要先加载“ Z”寄存器的间接加载。

结果,当编译器看到对固定I / O寄存器的内存操作时,它可能生成这些更有效的指令。但是,通过指针进行的正常加载/存储将具有相同的效果(尽管所需的时钟周期数不同)。对于不适合前64个扩展I / O寄存器(例如atmega328p上的OSCCAL),将始终生成正常的加载/存储指令。

,

简短答案-Atmel包含的标头中隐藏着一组宏,这些宏创建指向寄存器位置的指针。如果要查看任何源以及其他必要的标头(例如interrupt.h),它们位于WinAVR-20100110 / avr / include /

以下是该过程的简要概述:

您的Makefile定义了要使用的设备,然后将其定义传递给编译器。

DEVICE = atmega2560
...
-D__$(DEVICE)__

然后,您包含io.h,它会根据您的设备自动包含必要的标头:

// In main source file
#include <io.h>    

// In io.h
#include <avr/sfr_defs.h>
// ...
#elif defined (__AVR_ATmega2560__)
    #  include <avr/iom2560.h>

// In sfr_defs.h
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define __SFR_OFFSET 0x20
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

// In iom2560.h
#include <avr/iomxx0_1.h>
// Other device specific definitions

// Om iomxx0_1.h
#define PINA    _SFR_IO8(0X00)
// Other device family shared definitions

因此,如果您全部展开,则得到的是指向寄存器地址的易失性指针。每当您在代码中使用PINA时,预处理器都会将其替换为所有扩展的宏:

PINA
_SFR_IO8(0X00)
_MMIO_BYTE((0X00) + __SFR_OFFSET)
(*(volatile uint8_t *)((0X00) + 0x20))

哪个指定PINA是指向0x20的易失8位存储器地址的指针。然后,内部芯片架构会在每次访问该地址时将该地址映射到适当的外设寄存器。

不同的设备具有不同的寄存器地址和偏移量。如果要定义自己的数据,则需要查看相关的数据表。对于大多数AVR芯片,在结尾处有一个标题为“寄存器摘要”的部分,列出了所有寄存器地址和各个控制位的名称。以我的经验(至少对于AVR而言),数据表中的寄存器和位的名称与io.h文件中的定义完全相同。

还要注意使用“ uint8_t”而不是“ char”。通常(强烈建议)使用中的位宽特定定义来在适当的时候指定有符号/无符号和8/16/32位变量。由于AVR是8位的,因此任何使用16或32位(或浮点数)变量的操作都将需要多个时钟周期。在这种情况下,stdint.h应该具有:

typedef unsigned char uint8_t

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-