【经验】物联网创客指南:MCU设计的最佳实践和除错技巧(四)
在《物联网创客指南:MCU设计的最佳实践和除错技巧(三)》一文中,您了解了一些软件开发的基本实践和提示。SILICON LABS在本节继续讨论这些主题,包括内联函数,读取和写入闪存(包括用户页面闪存),以及如何避免缓冲区溢出。Silicon Labs将讨论类型转换的潜在问题,以及如何在出现问题时用配置锁解决问题。
9)内联函数的影响与限制
如果您在函数的前面定义关键字“inline”,则告诉编译器您希望将该函数复制到代码中,就像是直接输入代码而不是作为函数。作为程序员,你希望这可以加快代码执行速度。每当在C中调用函数时,输入参数的内容,在函数完成时,代码应该与返回地址一起被放置到栈上,然后代码跳转以执行函数。通过内联函数,您打算绕过该将数据移入和移出堆栈的时间。然而,C编译器或多或少地会用到这一技巧,因为小的函数将由编译器自动内联。编译器将忽略对大型函数的这种内联建议,编译器已经确定这些函数不会从加速中获益。
内联函数不能包含本地静态变量。由于函数内容要在代码中替换,因此对函数作用域私有的静态变量没有意义。内联函数没有作用域。
10)读写闪存的区别
在EFM32中从闪存的任何区域读取数据非常简单。可以使用指向内存地址的指针访问闪存,如:
volatile uint8_t* address = 0x1000;
uint8_t my_value =*address;
通过在地址变量前面放置星号,您可以解除引用指针,这将提供系统地址0x1000的字节大小的内容。从闪存检索的字节数将根据my_value的变量类型而有所不同。例如,定义为uint64_t的my_value将从地址0x1000到0x1007同时获取8个字节。因此,执行my_value ++作为64位无符号整数指向0x1008。
写入闪存是一个更复杂的业务。所有闪存将存储器组织成“页面”,其将可写入单元组合在一起,成为只能作为一组擦除的chuck。因此,如果要写入单个字节的非易失性存储器,则必须先擦除整个页面。对于程序员来说,这意味着你必须首先读取整个页面的内存,将它存储在RAM中,擦除页面,然后将修改的字节写回整个页面,你在操作的中间不能失去电源,否则你将永远失去了那个页面的数据。通常也有时间含义,因为擦除和写入页面比读取值需要更多的时间。当一页闪存被擦除时,该页的所有内容被复位为1,或在字节大小的存储器查看器中为0xFF。因此,当你写一个字节到内存,你只是清除那些不是1的位。
要写入EFM32闪存,另一个要求是,写入闪存的功能必须驻留在RAM中,而不是在正常执行代码所在的闪存中,在那里相同的代码正被执行。这要求任何意图写入闪存的函数也将被执行的RAMFUNC声明,就像在ramfunc.h库实用程序文件中定义的那样。
11)将持久数据存储在Flash用户页面闪存中
主闪存从地址0开始,并根据EFM32模型有着不同的容量上限。有关特定芯片的主闪存结束的位置,请查看参考手册。除了内置闪存,还有一个内存区域,用于外部闪存扩展,其位于内部主存储器之外,最高可达24MB。上一节中所述的任何读取或写入主闪存的操作都可用于访问主闪存。但是,每次对芯片编程时,或者如果器件被擦除,存储在程序使用的区域中的数据将被新的代码覆盖。因此,当执行闪存更新过程时,例如,在引导加载器中,通常使用对主闪存的读取或写入。您当然可以随意使用位于程序大小以上的闪存,只要您意识到如果芯片通过器件擦除操作或JTAG编程序列擦除,它将被擦除。
EFM32提供了一个单独的闪存存储器页,它不会被器件擦除操作或JTAG编程操作擦除。闪存的这个区域称为用户页,并从地址0x0FE00000开始。可用的内存量是单页,并且闪存页面的大小根据EFM32芯片的型号而变化。有关闪存页面的大小,请查看数据手册或参考手册。您可以将此页面用于从设备擦除操作(例如设备序列号,设备校准数据或每个设备特定的任何数据)开始,从引导到引导的设备上保留的任何内容。
12)避免缓冲区溢出和可能造成的破坏
当使用计算机开发代码时,您很幸运能在具有操作系统,内置文件系统和显示屏幕的系统上开发。当你的软件遇到麻烦,操作系统开始采取行动,帮助你,并通知你,你试图访问超出程序范围内的内存,或者你造成了一些故障。在开发嵌入式代码时,你没有这些帮助。没有操作系统来监视事情,以确保你的程序保持在一个很好定义的边界。如果你的代码决定写数据到地址零或一百万,MCU将尝试做它被告知的任何事情。它不能告诉你,你不知道你在做什么,因为没有限制你的程序可以做什么。你种情况有好坏两个方面。
嵌入式开发人员面临的一个大问题是缓冲区溢出。在嵌入式中工作意味着对存储器地址的大量直接操作。如果你的程序开始表现不规律,或者你看到一个看起来改变其值而不设置的变量,缓冲区溢出可能是罪魁祸首。
首先要注意的事是显而易见的。如果定义一个长x个字节的数组,请不要写超出x-1。如果在名为foo的数组中有x个项目,则只能将foo [0]映射到foo [x-1]。坏事是,如果你寻址foo [x],foo [x + 1],等等,你的代码仍然有效。 MCU将满意地写入任何其他变量恰好超出foo [x-1]的位置。然后你的项目开始出现问题。
当你调整指针,而将指针转换为不同的类型时,这也可以失去控制。例如:
void some_function()
{
// Array of just 4 bytes
uint8_t my_array[4];
// Pointer to group of 4 bytes
uint32_t * my_ptr =(uint32_t *) my_array;
// foo is assigned all 4 bytes of my_array,
int foo =*my_ptr;
// then my_ptr is incremented by one
// which to my_ptr's type means 4 bytes
// So my_ptr is now sitting out of bounds
my_ptr++;
}
以上演示了为什么使用在创建时是单向键入的结构,但稍后由不同类型的指针使用需要小心的原因。你可以做类型转换,但并不意味着你应该做。
另一种可以遇到缓冲区溢出问题的方法是忘记局部范围的限制。在C中,函数完成的大部分工作都是在传递给函数的指针上执行的。C函数的返回值通常被限制为“状态”字节,因为简单的值很容易从堆栈中的C函数返回,而其他结构很难返回。例如,一个简单的数组是局部的一个函数:
// This function will generate a compiler warning:
// warning: function returns address of local variable
int* some_function()
{
int my_array[4];
return my_array;
}
// This function has no warning!
int* some_other_function()
{
int my_array[4];
int* foo = my_array;
return foo;
}
一旦函数返回,分配给my_array的内存将被系统回收并分配给需要内存的下一个东西。如果从C函数返回一个指向本地数组的指针,编译器不会警告你,MCU会做你所要做的事情。如果在函数返回之后没有新的内存分配,代码有时会工作。你的解决方案将是间歇性的!
// This is a better way, pass pre-allocated pointers intothe function
//“int my_array[]” and “int * my_array” are identicalfor function parameters
void some_new_function(int my_array[])
{
my_array[0]= 1;
}
13)精通类型转换
当你在嵌入式开发中“触及核心”时,你会经常使用小量的信息,所以你最终经常使用有限的可变大小(例如8位寄存器)寄存器,从一种类型转换到另一种类型。本节的一些例子展示了在uint8_t类型和uint32_t类型上增加指针时,你必须注意对地址的影响。此外,还必须了解C编译器如何解释和转换类型。
将类型从一种类型转换到另一种类型实际上不执行任何转换。它不是一个函数,而只是一种告诉编译器如何解释数据的方法。当你分配不同类型的变量,C编译器应该警告你,但编译器并不总是警告你这一点。例如,考虑将有符号整数转换为无符号整数的情况,如下所示:
int8_t a = 0; // Range is -128 to +127
uint8_t b = 0; // Range is 0 to 255
long c = 0; // Range is huge, both positive and negative
a =-64; // a can be negative
b = 64; // b can only be positive
c = a + b; // c is 0
a = b; // Converting a signed to unsigned is OK, in this case
c = a - b; // c is again 0
b = 128; // b is now bigger than a can represent
a = b; // a is now converted to -128
c = a - b; // c = -128 - 128 = -256!
这个例子显示了当你的变量变大时会遇到的麻烦。事情工作正常一段时间,但然后当你的变量超过范围,他们开始失败。确保在转换类型时知道您想要什么。大小为8的uint不能容纳超过0到255范围的任何内容,8位的int不能容纳超出-128到+127的任何内容。简单地把一个大的uint8_t作为一个int8_t不能将你的int8_t变为一个更大的int。它仍然限制在+127。如果你想增加它的范围,你必须使用更大的类型,如int16_t。
14)使用配置锁来缓解故障代码的问题
有时候,当嵌入式工程师对指针进行数学运算时,我们会使用我们的软件来影响硬件。一种保护你的代码不因缓冲区溢出和其他糟糕的指针算法而造成严重错误的方法是使用配置锁。许多EFM32外设上可用的配置锁需要一个特殊值写入配置锁寄存器,以允许更改外设配置。这防止错误行为代码改变设备的整体配置。
编码成功的关键
从现有示例开始,然后使用命名良好的变量和大量注释记录您的更改。进一步了解C语言,并了解在何处以及如何声明变量和函数。不要尝试写入尚未擦除的闪存或超出变量可访问范围的内存。
在下一节中,将学习如何在SimplicityStudio IDE中更好地控制软件构建,敬请期待。
看到这里您是否又有项目灵感需实现,点击下面开发软件帮你忙。
- |
- +1 赞 0
- 收藏
- 评论 0
本网站所有内容禁止转载,否则追究法律责任!
相关推荐
【经验】EFM32系列低功耗32位MCU芯片加密详解
本文主要介绍以Silicon Labs EFM32系列低功耗32位MCU为核心的方案打造加密系统。
【经验】如何使用32位超低功耗MCU外设反射系统功能?
外设反射系统(PRS)是 Silicon Labs EFM32系列MCU专有的外设互联总线,它允许不同的外设无需CPU干预即可直接相互通信。
【经验】32位MCU的LDMA实现ADC至RAM的数据传输调试方法
Silicon Labs的EFM32PG/ EFM32JG系列32位MCU具有超低功耗、高性能、丰富的外设等特点,其 LDMA相比于series 0的DMA具有更多的传输方式,更适合应用于电池供电的产品。
世界上最节能的微控制器EFM32之十大低功耗奥秘
Silicon Labs EFM32 32 位微控制器系列是世界上最为节能的微控制器,特别适用于低功耗和能源敏感型应用,包括能源、水表和燃气表、楼宇自动化、警报及安防和便携式医疗/健身器材。本文着重强调10个Silicon Labs32位MCU功耗低的因素。
SILICON LABS 32-bit Microcontroller选型表
SILICON LABS 32位MCU选型,频率24MHz~80MHz,Flash存储4kB~2048kB,RAM存储2kB~512kB。
产品型号
|
品类
|
系列
|
Frequency(MHz)
|
Flash (kB)
|
RAM (kB)
|
Vdd min(V)
|
Vdd max(V)
|
Package Type
|
Package Size (mm)
|
Internal Osc.
|
Debug Interface
|
Cryptography
|
Dig I/O Pins
|
ADC 1
|
DAC
|
USB
|
Cap Sense
|
LCD
|
Temp Sensor
|
Timers (16-bit)
|
UART
|
USART
|
SPI
|
I2C
|
I2S
|
EMIF
|
RTC
|
Comparators
|
EFM32GG290F512-BGA112
|
32位MCU
|
EFM32 Giant Gecko
|
48
|
512
|
128
|
1.98
|
3.8
|
BGA112
|
10x10
|
±2%
|
ETM; SW
|
AES-128 AES-256
|
90
|
12-bit, 8-ch., 1 Msps
|
12-bit, 2 ch.
|
-
|
Cap Sense
|
-
|
Temp Sensor
|
4
|
7
|
3
|
3
|
2
|
1
|
0
|
RTC
|
2
|
选型表 - SILICON LABS 立即选型
SILICON LABS EFM32 Zero Gecko 32-bit Microcontroller选型表
SILICON LABS 32位MCU选型,基于24MHz Frequency,4kB~32kB Flash,2kB或4kB RAM等参数进行选型。
产品型号
|
品类
|
Frequency(MHz)
|
Flash (kB)
|
RAM (kB)
|
Dig I/O Pins
|
ADC 1
|
DAC
|
Cap Sense
|
Temp Sensor
|
Timers (16-bit)
|
AES-128
|
UART
|
USART
|
SPI
|
I2C
|
I2S
|
EMIF
|
RTC
|
Comparators
|
Vdd min(V)
|
Vdd max(V)
|
Package Type
|
Package Size (mm)
|
Internal Osc.
|
Debug Interface
|
EFM32ZG222F8-QFP48
|
Microcontroller
|
24
|
8
|
2
|
37
|
12-bit, 4-ch., 1 Msps
|
—
|
Cap Sense
|
Temp Sensor
|
2
|
AES-128
|
2
|
1
|
1
|
1
|
1
|
0
|
RTC
|
1
|
1.98
|
3.8
|
QFP48
|
7x7
|
±2%
|
SW
|
选型表 - SILICON LABS 立即选型
超低功耗MCUEFM32能耗模式探讨
世强代理的Silicon Labs EFM32 MCU 旨在在低能耗模式下实现高度的自主运行。该MCU 智能结合了外围设备、低漏电 RAM、数据保持、DMA 和互联能力、低功耗振荡器以及极短的唤醒时间,因此长时间在低能耗模式下的运行效果很好,大幅降低了能耗。
Silicon Labs 32位低功耗MCU EFM32JG1P在故障指示器的应用中,GPIO拉低后,电平为0.8V,而不是0V,根本原因是什么?
Silicon Labs 32位低功耗MCU EFM32JG1P GPIO拉低后,电平为0.8V,主要是因为底下大焊盘PIN0没有连接到GND或者与GND接触不良造成。
如何为Silicon Labs 32位低功耗MCU EFM32GG230F512实现基于RTC的软件无滴答日历?
在Silicon Labs 32位低功耗MCU EFM32GG230F512上实现软件方式的无滴答日历,可以通过使用日历标准C库 time.h, 在time.h中实现的timer()函数基于RTC计数值来计 算日历时间。
【应用】EFM32TG11低功耗32位MCU在超声波水表上的应用
Silicon Labs的32位MCU EFM32TG11,最高主频48MHz,37uA/MHz的全速运行功耗,1.3uA休眠EM2模式功耗,集成低功耗LCD驱动,最大128KB大容量Flash,32PIN到80PIN多种可选封装,将助力于超声波水表超低功耗,多功能,低成本设计。
Silicon Labs 32位低功耗MCU EFM32GG990芯片使用中,将OPAMP(运算放大器)组合成三运放差分放大器时可选的放大倍数为多少?
当将三个OPAMP组合成差分放大器时,差分放大器的增益是由OPA0和OPA1的组合增益所决定的,由于三运放差分电路的电阻网络对应的桥臂要对应成比例,所以只三种有 效的差分增益可供使用,它们分别为1/3、1和3,此时OPA0 RESSEL和OPA2 RESSEL的组合分别为0和4、1和1、4和0。
如何读取EFM32系列32位MCU的多通道AD扫描采样数据?
EFM32系列单片机ADC的数据保存寄存器有两个,一个是单通道模式的采样数据保存寄存器,一个是扫描模式的采样数据保存寄存器。如果使用多通道扫描采样,那么每个通道采样完成后会产生一个ADC扫描模式中断,用户需要在中断里面读取该通道的扫描采样数据以便存储下一个通道的采样数据。建议使用DMA读取方式,将DMA的触发源设置为ADC的扫描模式事件,这样就可以实现不用进入ADC中断以及MCU的参与即可完成ADC扫描采样数据的保存和读取。
32位低功耗MCU EFM32G232F64,其ADC的Single模式和SCAN模式有什么区别?
32位低功耗MCU EFM32G232F64的ADC,使用Single模式一次只能转换一个通道,其转换结果保存在ADCn_SiNGLEDATA寄存器中;使用SCAN模式一次可以转换多个通道,其转换结果保存在ADCn_SCANDATA寄存器。
【应用】低功耗32位MCU助力热量表无线传输,功耗低至64uA/MHz
Silicon Labs新近推出了EFM32JG系列32位MCU产品,以其超低功耗、内置加密引擎等优势,很适合应用在电子式、无线化热量表产品中。
Silicon Labs 32位低功耗MCU EFM32G232F128系列微控制器GPIO的最大翻转速度为多少?
EFM32G232F128系列微控制器GPIO的最大翻转速度为系统时钟的1/6。例如,当主频为32MHz时,GPIO最大翻转速度大约为5.3MHz。
电子商城
现货市场
登录 | 立即注册
提交评论