在嵌入式系统上,我们有一个设置,允许我们通过命令行界面读取任意数据以进行诊断。对于大多数数据,这很好用,我们使用memcpy()
在请求的地址复制数据并通过串行连接将其发回。
但是,对于16位硬件寄存器,memcpy()
会导致一些问题。如果我尝试使用两个8位访问来访问16位硬件寄存器,则高位字节无法正确读取。
有没有人遇到过这个问题?我是一个'高级'(C#/ Java / Python / Ruby)家伙,它正在向硬件靠拢,这是外星人的领域。
处理这个问题的最佳方法是什么?我看到了一些信息,特别是[帖子here对我有点困惑。这篇文章的作者完全我遇到了同样的问题,但我讨厌在没有完全理解我正在做的事情的情况下实现解决方案。
非常感谢你能解决这个问题。谢谢!
答案 0 :(得分:4)
此硬件中的每个寄存器都作为双字节数组公开,第一个元素以双字节边界对齐(其地址为偶数)。 memcpy()运行一个循环并在每次迭代时复制一个字节,因此它以这种方式从这些寄存器复制(所有循环展开,char是一个字节):
*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd
但是,出于某些硬件特定原因,第二行无法正常工作。如果一次复制两个字节而不是一次复制一个字节,则以这种方式完成(short int是两个字节):
*((short int*)target) = *((short*)register;// evenly aligned
在这里,您可以在一个操作中复制两个字节,并且第一个字节均匀对齐。由于没有从奇怪对齐的地址进行单独复制,因此可以正常工作。
修改后的memcpy会检查地址是否经过精心对齐,如果是,则会以两个字节为单位进行复制。
答案 1 :(得分:4)
除了Eddie said之外,您通常需要使用易失性指针来读取硬件寄存器(假设存储器映射寄存器,所有系统都不是这种情况,但对您来说听起来是这样的) )。类似的东西:
// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)
uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd; // whatever the reg address is
uint16_t val = *pReg; // read the 16-bit wide register
以下是Dan Saks的一系列文章,它们应该为您提供几乎所有需要知道的内容,以便能够在C / C ++中有效地使用内存映射寄存器:
答案 2 :(得分:3)
如果您需要访问特定大小的硬件寄存器,那么您有两种选择:
读取硬件寄存器会产生副作用,具体取决于寄存器及其功能,因此访问具有适当大小访问权限的硬件寄存器非常重要,这样您就可以一次性读取整个寄存器。
答案 3 :(得分:2)
我认为所有细节都包含在您发布的帖子中,所以我会尝试将其分解一下;
具体而言;
If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.
因此,问题在于,如果在单个16位读取中读取它们的值,则硬件寄存器仅报告正确的值。这相当于做;
uint16 value = *(regAddress);
使用单个16字节读取从地址读取到值寄存器。另一方面,你有memcpy,它一次复制数据一个字节。喜欢的东西;
while (n--)
{
*(uint8*)pDest++ = *(uint8*)pSource++;
}
因此,这会导致寄存器一次读取8位(1个字节),导致值无效。
该线程中发布的解决方案是使用memcpy版本,该版本将使用16位读取复制数据,无论源和目标是否为6位对齐。
答案 4 :(得分:2)
通常使用与寄存器大小相同的整数类型就足够了。在大多数编译器上,短路为16位。
void wordcpy(short *dest, const short *src, size_t bytecount)
{
int i;
for (i = 0; i < bytecount/2; ++i)
*dest++ = *src++;
}
答案 5 :(得分:1)
你需要知道什么?你已经找到了一个单独的帖子来解释它。显然,CPU文档要求通过16位读写访问16位硬件寄存器,但memcpy的实现使用8位读/写。所以他们不能一起工作。
解决方案是不使用memcpy来访问该寄存器。 相反,编写自己的例程来复制16位值。
答案 6 :(得分:1)
不确定问题是什么 - 我认为该帖子有正确的解决方案。 正如您所说,问题是标准的memcpy()例程一次读取一个字节,这对于内存映射的硬件寄存器无法正常工作。这是处理器的限制 - 根本无法获得有时读取字节的值。
建议的解决方案是编写自己的memcpy(),它只适用于字对齐的地址,并且一次读取16位字。这非常简单 - 链接提供了c和汇编版本。唯一的问题是确保始终从有效对齐的地址执行16位副本。您可以通过两种方式执行此操作:使用链接器命令或编译指示来确保事物已对齐,或者为未对齐缓冲区前面的额外字节添加特殊情况。