问题描述
我正在尝试使用Sourcetrail(https://www.sourcetrail.com/)快速进入pic18系列微控制器的一些旧的嵌入式c / c ++源代码。
导入硬件包含文件时出现错误,该文件使用奇异的方法定义可位寻址的硬件寄存器的硬件地址,例如const uniqueDates = [...new Set(sortbydate)];
console.log(uniqueDates);
const arr1 = [];
const arr2 = [];
function FindDateObje() {
for (let i = 0; i < sortbydate.length; i++) {
if (sortbydate[i] == uniqueDates[0]) {
arr1.push(data[i]);
} else {
arr2.push(data[i]);
}
}
return [arr1,arr2];
}
console.log(FindDateObje());
中的以下内容。
pic18f26k22.h
您可能已经猜到了,typedef union {
struct {
unsigned ANSA0 :1;
unsigned ANSA1 :1;
unsigned ANSA2 :1;
unsigned ANSA3 :1;
unsigned :1;
unsigned ANSA5 :1;
};
} ANSELAbits_t;
extern volatile ANSELAbits_t ANSELAbits @ 0xF38;
部分使SourceTrail感到困惑,并且期望只是一个分号。嵌入式系统的许多其他c / c ++编译器都使用该方法,因此我认为存在一个简单的修复方法。
编辑:
首先,要弄清楚:@用于将volatile变量作为位或字节地址放置在内存映射中的特定位置。 (与8086 cpu具有内存和IO寻址系统的方式略有相似)。在这种情况下,MPLab c / c ++编译器附带了全局包含(用于数百个不同的微控制器)。为了便于分析,我可以复制全局包含文件,并在SourceTrail中设置到全局包含的其他路径-以便可以根据需要进行修改。我希望不要触摸项目文件,因为它们仍需要在原始设置中进行编译。
在尝试@Antti Haapala回答时,我发现需要考虑以下类型的用法:
@ 0xF38
我找不到在任何地方定义的extern volatile unsigned char BAUDCON1 @ 0xFB8;
#ifndef BANKMASK
#define BANKMASK(addr) ((addr)&0FFh)
#endif
extern volatile __bit ABDEN1 @ (((unsigned) &BAUDCON1)*8) + 0;
#define ABDEN1_bit BANKMASK(BAUDCON1),0
,但这是一个特殊的结构,用于保存该位的位地址(而不是字节地址)。
解决方法
@在C中不是有效的令牌,因此您也不能将其用作宏标识符。最简单的解决方案是使用一个宏来处理@地址,即
#ifdef somethingsomething
#define AT(address) @ address
#else
#define AT(address)
#endif
extern volatile ANSELAbits_t ANSELAbits AT(0xF38);
第一个定义应由仅在目标上使用的宏保护。使用简单的Perl脚本(如
)进行更改应该非常容易perl -pi -e 's/@\s*([0-9a-fA-FxX]+)/AT($1)/g' *.c *.h
如果此@
语法按原样在供应商提供的头文件中使用,则感到羞耻。
对于Microchip C18编译器:
#if defined MCHP_C18
#define AT(address) @ address
#else
#define AT(address)
#define __bit _Bool
#endif
然后重写:
extern volatile __bit ABDEN1 @ (((unsigned) &BAUDCON1)*8) + 0;
为
extern volatile __bit ABDEN1 AT((((unsigned) &BAUDCON1)*8) + 0);
然后,当未定义MCHP_C18
时,它将预处理为:
extern volatile _Bool ABDEN1;
_Bool
与__bit
当然并不完全相同,但具有足够接近的语义行为,也许对此有用。
因此,@
令牌的目的是使变量位于特定地址?那么为什么没有像宏这样的变量通过硬编码指针访问内存呢?
#define ANSELAbits (* (ANSELAbits_t *) 0xF38)
或者,如果您不介意在源代码中撒满*
运算符,则可以摆脱该宏:
static ANSELAbits_t * const ANSELAbits_ptr = (ANSELAbits_t *) 0xF38;
在有人抱怨UB之前。是的,将整数转换为指针不是可移植的,但是应该可以在大多数读取特定内存地址的架构上使用。
我个人更喜欢指针变体。 *
和->
运算符明确表明您正在访问特定内存位置的内容:
// This is in my humble opinion clearer code:
ANSELAbits_ptr->ANSA0 = 1;
// or
(* ANSELAbits_ptr).ANSA0 = 1;
// ...rather than this:
ANSELAbits.ANSA0 = 1; // How do we know that ANSELAbits is "Magic"?