AVR-GCC里定义的API,以及AVR-GCC的工作过程_自定义库 avr-gcc-程序员宅基地

技术标签: 汇编  编译器  [avr]  api  存储  [嵌入式系统]  工作  [单片机]  flash  

 

AVR-GCC里定义的API

        嵌入式编程的代码可以简单地分为两部分,一是与硬件无关的算法部分,对其编程与普通C编程没有区别;二是与硬件相关的寄存器/端口操作部分。不同的MCU实现方法各有不同。在AVR-GCC里则通过一系列的API来解决。当然,用户也可以定义自己的API。在此简单地介绍目前AVR-GCC里定义的API,以及AVR-GCC的工作过程。

一.应用程序启动过程(Start Up)
标准库文件包含一个启动模块(Start Up Module),用于为真正执行用户程序做环境设置。
启动模块完成的任务如下:
1. 提供缺省向量表
2. 提供缺省中断程序入口
3. 初始化全局变量
4. 初始化看门狗
5. 初始化寄存器MCUCR
6. 初始化数据段
7. 将数据段.bss的内容清零
8. 跳转到main()。(不用调用方式,因为main()不用返回)
启动模块包含缺省中断向量表,其内容为预先定义好的函数名称。这些函数名称可以由程序员重载。中断向量表的第一个内容为复位向量,执行结果是将程序跳转到_init_。在启动模块里,_init_表示的地址与_real_init_指向的地址相同。如果要加入客户代码,则需要在程序里定义一个_init_函数。在此函数的末尾跳转到_real_init_。具体实现如下:
void _real_init_(void);
void _init_(void) __attribute__((naked));
void _init_(void)
{
 // 用户代码
 
 // 最后的代码必须为:
 asm ("rjmp _real_init_");
}

在_real_init_部分,系统将设置看门狗和MCUCR寄存器。启动模块并没有真正取用相应寄存器的设置数值(以符号_init_wdctr_,_init_mcucr_,_init_emcucr_表示),而是通过地址来取得其值。因而用户可以通过链接器的--defsym选项来设置这些符号的地址。如果用户没有定义,则启动模块将使用缺省值。
接下来系统将从程序存储器里把具有初值的全局变量加载到数据存储器SRAM。然后是将数据段.bbs清零。此数据段包含所有没有的初值的非AUTO变量。
最后,系统跳转到main()函数,用户代码开始执行。系统对此特殊函数加入一些特殊的处理。进入此函数后,堆栈指向SRAM的末尾。

二.存储器API
AVR具有三种存储器:FLASH,SRAM和EEPROM。AVR-GCC将程序代码放在FLASH,数据放在SRAM。
I.程序存储器
如果要将数据(如常量,字符串,等等)放在FLASH里,用户需要指明数据类型__attribute__((progmem))。为了方便使用,AVR-GCC定义了一些更直观的符号,如下表所示。
类型 定义
prog_void void __attribute__((progmem))
prog_char char __attribute__((progmem))
prog_int int __attribute__((progmem))
prog_long long __attribute__((progmem))
prog_long_long long long __attribute__((progmem))
PGM_P prog_char const*
PGM_VOID_P prog_void const*
提供的库函数有:
1.__elpm_inline
用法:uint8_t __elpm_inline(uint32_t addr);
说明:执行ELPM指令从FLASH里取数。参数为32位地址,返回一个8位数据。
2.__lpm_inline
用法:uint8_t __elpm_inline(uint16_t addr);
说明:执行LPM指令从FLASH里取数。参数为16位地址,返回一个8位数据。
3.memcpy_P
用法:void* memcpy_P(void* dst, PGM_VOID_P src, size_t n);
说明:memcpy的特殊版本。完成从FLASH取n个字节的任务。
4.PRG_RDB
用法:uint8_t PGR_RDB(uint16_t addr);
说明:此函数简单地调用__lpm_inline
5.PSTR
用法:PSTR(s);
说明:参数为字符串。功能是将其放在FLASH里并返回地址。
6.strcmp_P
用法:int strcmp(char const*, PGM_P);
说明:功能与strcmp()类似。第二个参数指向程序存储器内的字符串。
7.strcpy_P
用法:char* strcpy_P(char*, PGM_P);
说明:功能与strcpy()类似。第二个参数指向程序存储器内的字符串。
8.strlen_P
用法:size_t strlen_P(PGM_P);
说明:功能与strlen()类似。第二个参数指向程序存储器内的字符串。
9.strncmp_P
用法:size_t strncmp_P(char const*, PGM_P, size_t);
说明:功能与strncmp()类似。第二个参数指向程序存储器内的字符串。
10.strncpy_P
用法:size_t strncpy_P(char*, PGM_P, size_t);
说明:功能与strncpy()类似。第二个参数指向程序存储器内的字符串。
II.EEPROM
AVR内部有EEPROM,但地址空间与SRAM的不相同。在访问时必须通过I/O寄存器来进行。EEPROM API封装了这些功能,为用户提供了高级接口。使用时要包含eeprom.h。在程序里定义EEPROM数据的例子如下:
static uint8_t variable_x __attribute__((section(".eeprom"))) = 0;
不同的AVR器件具有不同数目的EEPROM。链接器将针对不同的器件分配存储器空间。
1. eeprom_is_ready
用法:int eeprom_is_ready(void);
说明:此函数用于指示是否可以访问EEPROM。如果EEPROM正在执行写操作,则在4ms内无法访问。此函数查询相应的状态位来指示现在是否可以访问EEPROM。
2. eeprom_rb
用法:uint8_t eeprom_rb(uint16_t addr);
说明:从EEPROM里读出一个字节的内容。参数addr用于指示要读出的地址。_EEGET(addr)调用此函数。
3. Eeprom_read_block
用法:void eeprom_read_block(void* buf, uint16_t addr, size_t n);
说明:读出一块EEROM的内容。参数addr为起始地址,n表明要读取的字节数。数据被读到SRAM的buf里。
4. eeprom_rw
用法:unint16_t eeprom_rw(uint16_t addr);
说明:从EEPROM里读出一个16位的数据。低字节为低8位,高字节为高8位。参数addr为地址。
5. eeprom_wb
用法:void eeprom_wb(uint16_t addr, uint8_t val);
说明:将8位数据val写入地址为addr的EEPROM存储器里。_EEPUT(addr,val)调用此函数。

三.中断API
由于C语言设计目标为硬件无关,因此各种编译器在处理中断时使用的方法都是编译器设计者自己的方法。
在AVR-GCC环境里,向量表已经预先定义,并指向具有预定义名称的中断例程。通过使用合适的名称,用户例程就可以由相应的中断所调用。如果用户没有定义自己的中断例程,则器件库的缺省例程被加入。
除了中断向量表的问题,编译器还必须处理相关寄存器保护的问题。中断API解决了细节问题。用户只要将中断例程定义为INTERRUPT()或SIGAL()即可。而对于用户没有定义的中断,缺省例程的处理是reti指令。
函数定义可参见interrupt.h,中断信号符号表参见sig-avr.h。
1. cli
用法:void cli(void);
说明:通过置位全局中断屏蔽位来禁止中断。其编译结果仅为一条汇编指令。
2. enable_external_int
用法:void enable_external_int(uint8_t ints);
说明:此函数访问GIMSK寄存器(对于MEGA器件则是EIMSK寄存器)。功能与宏outp()一样。
3. INTERRUPT
用法:INTERRUPT(signame)
说明:定义中断源signame对应的中断例程。在执行时,全局屏蔽位将清零,其他中断被使能。ADC结束中断例程的例子如下所示:
INTERRUPT(SIG_ADC)
{
}
4. sei
用法:void sei(void);
说明:通过清零全局中断屏蔽位来使能中断。其编译结果仅为一条汇编指令。
5. SIGNAL
用法:SIGNAL(signame)
说明:定义中断源signame对应的中断例程。在执行时,全局屏蔽位保持置位,其他中断被禁止。ADC结束中断例程的例子如下所示:
SIGNAL(SIG_ADC)
{
}
6. timer_enable_int
用法:void timer_enable_int(uint8_t ints);
说明:此函数操作TIMSK寄存器。也可以通过outp()来设置。
四.I/O API
I.I/O端口API
1. BV
用法:BV(pos);
说明:将位定义转换成屏蔽码(MASK)。与头文件io.h里的位定义一起使用。例如,置位WDTOE和WDE可表示为“BV(WDTOE) | BV(WDE)”
2. bit_is_clear
用法:uint8_t bit_is_clear(uint8_t port, uint8_t bit);
描述:如果port的bit位清零则返回1。此函数调用sbic指令,故port应为有效地址。
3.bit_is_set
用法:uint8_t bit_is_set(uint8_t port, uint8_t bit);
描述:如果port的bit位置位则返回1。此函数调用sbis指令,故port应为有效地址。
4.cbi
用法:void cbi(uint8_t port, uint8_t bit);
说明:清零port的bit位。bit的值为0~7。如果port为实际I/O寄存器,则此函数生成一条cbi指令;否则,函数生成相应的优化代码。
5.inp
用法:uint8_t inp(uint8_t port);
说明:从端口port读入8比特的数值。如果port为常数,则函数生成一条in指令;若为变量,则函数用直接寻址指令。
6.__inw
用法:uint16_t __inw(uint8_t port);
说明:从I/O寄存器读入16位的数值。此函数用于读取16位寄存器(ADC,ICR1,OCR1,TCNT1)的值,因为读取这些寄存器需要合适的步骤。由于此函数只产生两条汇编指令,因此要在中断禁止时使用,否则有可能由于中断插入到指令之间造成读取错误。
7.__inw_atomic
用法:uint16_t __inw_atomic(uint8_t port);
说明:以原子语句方式读取16位I/O寄存器的数值。此函数首先禁止中断,读取数据之后再恢复中断状态,因此可以安全地应用在各种系统状态。
8.loop_until_bit_is_clear
用法:oidoid loop_until_bit_is_clear (uint8_t port, uint8_t bit);
说明:此函数简单地调用sbic指令来测试端口port的bit位是否清零。port必须为有效端口。
9.loop_until_bit_is_set
用法:oidoid loop_until_bit_is_set (uint8_t port, uint8_t bit);
说明:此函数简单地调用sbis指令来测试端口port的bit位是否置位。port必须为有效端口。
10.outp
用法:void outp(uint8_t val, uint8_t port);
说明:将val写入端口port。如果port为常数,则函数生成一条out指令;若为变量,则函数用直接寻址指令。
11.__outw
用法:void __outw(uint16_t val, uint8_t port);
说明:将16位的val写入端口port。此函数适合于操作16位寄存器,如ADC,ICR1,OCR1,TCNT1。由于此函数只产生两条汇编指令,因此要在中断禁止时使用,否则有可能由于中断插入到指令之间造成读取错误。
12.__outw_atomic
用法:void __outw_atomic(uint16_t val, uint8_t port);
说明:将16位的val写入端口port。此函数适合于操作16位寄存器,如ADC,ICR1,OCR1,TCNT1。此函数首先禁止中断,读取数据之后再恢复中断状态,因此可以安全地应用在各种系统状态。
13.sbi
用法:void sbi(uint8_t port, uint8_t bit);
说明:置位port的bit位。bit的值为0~7。如果port为实际I/O寄存器,则此函数生成一条 sbi指令;否则,函数生成相应的优化代码。
五.看门狗API
以下函数操作看门狗。宏定义参见wdt.h。
用户可以通过起动代码初始化看门狗。WDTCR的缺省值为0。如果你希望将其设置为其他值,则需要在链接命令里加入相应的命令。使用的符号为__init_wdtcr__。如下为将WDTCR设置为0x1f的例子:
avr-ld –defsym __init_wdtcr__=0x1f
1. wdt_disable
用法:void wdt_disable(void);
说明:关闭看门狗。
2. wdt_enable
用法:void wdt_disable(unit8_t timeout);
说明:使能看门狗。看门狗溢出时间为timeout。
timeout 周期
0 16K CLK
1 32K CLK
2 64K CLK
3 128K CLK
4 256K CLK
5 512K CLK
6 1024K CLK
7 2048K CLK

3. wdt_reset
用法:void wdt_reset(void);
说明:产生喂狗指令wdr。
 
附录:AVR-GCC配置
汇编选项
选项 描述
-mmcu=name 指定目标器件
name可以为:at90s1200,at90s2313,at90s2323,at90s2333,at90s2343,at904433,at90s8515,at90s8535,atmega103,atmega161
寄存器使用
如果用户需要进行汇编与C的混合编程,必须了解寄存器的使用。
1.寄存器使用
r0  可用做暂时寄存器。如果用户汇编代码使用了r0,且要调用C代码,则在调用之前必须保存r0。C中断例程会自动保存和恢复r0。
r1  C编译器假定此寄存器内容为“0”。如果用户使用了此寄存器,则在汇编代码返回之前须将其清零。C中断例程会自动保存和恢复r1。
r2-r17,r28,r29  C编译器使用这些寄存器。如果用户汇编代码需要使用这些寄存器,则必须保存并恢复这些寄存器。
R18-r27,r30,r31 如果用户汇编代码不调用C代码则无需保存和恢复这些寄存器。如果用户要调用C代码,则在调用之前须保存。
2.函数调用规则
参数表:函数的参数由左至右分别分配给r25到r8。每个参数占据偶数个寄存器。若参数太多以至r25到r8无法容纳,则多出来的参数将放入堆栈。
返回值:8位返回值存放在r24。16位返回值存放在r25:r24。32位返回值存放在r25:r24:r23:r22。64位返回值存放在r25:r24:r23:r22:r21:r20:r19:r18。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jhonguy/article/details/7030861

智能推荐

ZwQuerySystemInformation 查看系统进程信息_zwduplicateobject系统进程-程序员宅基地

文章浏览阅读580次。#include typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, Syst_zwduplicateobject系统进程

深入理解Java虚拟机-第3章-垃圾收集器_从整体来看是基于“标记--整理”算法实现的收集器;从局部上来看是基于“复制”-程序员宅基地

文章浏览阅读118次。JVM的垃圾收集器经历了多代的发展,从单线程收集器到多线程收集器。一、背景垃圾回收器经历过多代的发展,从单线程到多线程,垃圾收集器的大家族产品如下,每一个连线表示可以组合使用。接下来大概分为几个阶段详细介绍下各个垃圾回收器的特点:阶段收集器名称区域并行/串行/并发算法优缺点适用场景第一阶段Serial新生代串行复制响应速度快单CPU环境下的Client模式Serial Old老年代串行标志-整理响应速度快单CPU环境下的Client._从整体来看是基于“标记--整理”算法实现的收集器;从局部上来看是基于“复制”

反弹shell-程序员宅基地

文章浏览阅读1.7w次,点赞19次,收藏92次。一、课时目标1.理解shell的概念2.理解交互式shell和非交互式shell的区别3.掌握反弹shell的原理和方法_反弹shell

做crud遇到的问题_this.crud.toquery()-程序员宅基地

文章浏览阅读363次。1.crud的流程1 流程: servlet调service进行业务处理service调dao完成数据库操作dao在把数据库操作结果在给serviceservice继续处理在把结果返回给servletservlet在根据service的处理结果选择是请求转发还是重定向来显示页面2.登录功能效验实现2 流程(登录功能效验实现):servlet层中UserServlet调UserServiceImplUserServiceImpl调UserDaoImplUserDaoImpl操作数据_this.crud.toquery()

sqlite database is locked 问题解决方案-程序员宅基地

文章浏览阅读5.1w次,点赞3次,收藏21次。这两天在项目中用大强度大频率的方法测试时遇到sqlite报database is locked的问题,分析下来原因是sqlite对数据库做修改操作时会做(文件)锁使得其它进程同一时间使用时会报该错误(也就是SQLITE_BUSY),但如果仅是多进程或多线程查询sqlite是支持的。也有可能是做sql开启事务查询等发生异常,数据库没有关闭,然后再去打开就锁定了解决方法有:1、使用进..._database is locked

PKI常见证书格式和转换_pkcs#10 pfx-程序员宅基地

文章浏览阅读1.1k次。PKCS 全称是 Public-Key Cryptography Standards ,是由 RSA 实验室与其它安全系统开发商为促进公钥密码的发展而制订的一系列标准,PKCS 目前共发布过 15 个标准。 常用的有:PKCS#7 Cryptographic Message Syntax StandardPKCS#10 Certification Request StandardP..._pkcs#10 pfx

随便推点

ESD二极管ESD05V56T-2L参数_esd二极管容值-程序员宅基地

文章浏览阅读358次。      ESD05V56T-2L的参数:  封装:SOT-563  电压:5V  钳位电压:9.8V  容值:3pF  功率:100W  ESD05V56T-2L的特性:  1、依据(tp=8/20μs)线路,峰值脉冲功率为100W  2、保护两个I/O线及电源线  3、适合高速接口的低电容  4、工作电压:5V  5、超小型封装要求小于2.9mm2的PCB面积  6、IEEE1394高速火线端口  6、IEC61000-4-2(ESD)±15_esd二极管容值

vim 配置-程序员宅基地

文章浏览阅读381次。1.安装git:sudoapt-get install git2.安装Bundle:gitclone https://github.com/gmarik/vundle.git~/.vim/bundle/vundle3.在.vimrc文件中添加如下语句4.自动安装插件wget -qO- https://raw.github.com/ma6174/vim/master/setup.sh | sh..._bundle 'last_edit_marker.vim

重生-程序员宅基地

文章浏览阅读479次。【内容】在提高班什么才是最有价值的? 在跟一些同学聊天的过程中,很多同学说不会干太长时间的计算机的,也包括最近在读的李笑来老师的重生-七年就是一辈子,每七年就会获得一项新的技能,包括Bill经常给我们分享的内容,还有自己进提高班的初衷,让我更加深刻地意识到,提高班根本不是单纯地学习计算机知识的地方,大家想想,像现在知识爆棚的时代,每半年就有1/2的知识被淘汰,如果我们以学习知识为目的,那无疑我们是

Extjs GridPanel用法详解_<ext:gridpanel-程序员宅基地

文章浏览阅读4.1k次。Extjs GridPanel 提供了非常强大数据表格功能,在GridPanel可以展示数据列表,可以对数据列表进行选择、编辑等。创建GridPanel要使用GridPanel,首先要定义Store,而在创建Store的时候必须要有Model,因此我们首先来定义Model://1.定义ModelExt.define("MyApp.model.User", { extend: "Ext.d..._

负载均衡500/502/504错误排查_springboot部署后有一个页面一直504-程序员宅基地

文章浏览阅读6.8k次。对于配置负载均衡之后,访问网站出现500 Internal Server Error, 502 Bad Gateway, 504 Gateway Timeout等错误,有可能由多种原因导致,例如运营商拦截,客户端异常行为导致防火墙封杀,负载均衡配置错误或者健康检查失败,后端Web应用访问问题等。可能原因以及解决方案一、源站域名没有备案或者域名没有在高防或者安全网络配置七层转发 解决方案:请将域_springboot部署后有一个页面一直504

鸿蒙手机系统会是什么样的,[财经]鸿蒙发布会主要内容2021新消息解读:OS手机操作系统是什么样的? - 南方财富网...-程序员宅基地

文章浏览阅读110次。2021 年 6 月 2 日,在备受瞩目的 HarmonyOS 2 及华为全场景新品发布会上,华为正式发布了数款出厂搭载鸿蒙 OS(HarmonyOS)的智能手机新品:Mate 40 系列、Mate X2 和 Nova 8 Pro 的新版本——同时,华为 P50 系列手机新品也在发布会上正式亮相。对于 P50 系列,余承东表示:这是一款伟大的产品,伟大的产品值得期待。除了上述智能手机新品,华为也在..._鸿蒙手机系统内容是什么样的

推荐文章

热门文章

相关标签