假设我们想在地址(比如0xc000 )上写,我们可以在C中定义一个宏为:
#define LCDCW1_ADDR 0xc000
#define READ_LCDCW1() (*(volatile uint32_t *)LCDCW1_ADDR)
#define WRITE_LCDCW1(val) ((*(volatile uint32_t *)LCDCW1_ADDR) = (val))我的问题是,当使用任何微控制器时,考虑一个例子MSP430,P1OUT寄存器地址是0x0021。
但是,当我们使用P1OUT=0xFFFF;//时,它会为P1OUT赋值0xFFFF。
我的问题是它是如何写到那个地址的,例如在0x0021这个例子中。IDE是IAR。我在标题msp430g2553.h 430g2553.h中找到了以下定义:
#define P1OUT_ (0x0021u) /* Port 1 Output */
DEFC( P1OUT , P1OUT_)我想它是在定义地址,但是其他要写或读的宏在哪里呢?
有谁能解释一下P1OUT在那个特定的地址位置是如何写的流吗?还有,让我知道你在0x0021u是什么意思?
谢谢
到目前为止,我发现的细节如下:
在msp430g2553.h 430g2553.h
#ifdef __IAR_SYSTEMS_ICC__
#include "in430.h"
#pragma language=extended
#define DEFC(name, address) __no_init volatile unsigned char name @ address;
#define DEFW(name, address) __no_init volatile unsigned short name @ address;
#define DEFXC volatile unsigned char
#define DEFXW volatile unsigned short
#endif /* __IAR_SYSTEMS_ICC__ */
#ifdef __IAR_SYSTEMS_ASM__
#define DEFC(name, address) sfrb name = address;
#define DEFW(name, address) sfrw name = address;
#endif /* __IAR_SYSTEMS_ASM__*/
#define P1OUT_ (0x0021u) /* Port 1 Output */
DEFC( P1OUT , P1OUT_)碘430g2553.h表示
__no_init volatile union
{
unsigned char P1OUT; /* Port 1 Output */
struct
{
unsigned char P0 : 1; /* */
unsigned char P1 : 1; /* */
unsigned char P2 : 1; /* */
unsigned char P3 : 1; /* */
unsigned char P4 : 1; /* */
unsigned char P5 : 1; /* */
unsigned char P6 : 1; /* */
unsigned char P7 : 1; /* */
}P1OUT_bit;
} @0x0021;有人能解释一下上面的定义是干什么的吗?我在MSP430 IAR /C++编译器中找到的详细信息:
Example of using __write and __read
The code in the following examples use memory-mapped I/O to write to an LCD
display:
__no_init volatile unsigned char LCD_IO @ address;
size_t __write(int Handle, const unsigned char * Buf,
size_t Bufsize)
{
size_t nChars = 0;
/* Check for stdout and stderr
(only necessary if file descriptors are enabled.) */
if (Handle != 1 && Handle != 2)
{
return -1;
}
for (/*Empty */; Bufsize > 0; --Bufsize)
{
LCD_IO = * Buf++;
++nChars;
}
return nChars;
}
The code in the following example uses memory-mapped I/O to read from a keyboard:
__no_init volatile unsigned char KB_IO @ 0xD2;
size_t __read(int Handle, unsigned char *Buf, size_t BufSize)
{
size_t nChars = 0;
/* Check for stdin
(only necessary if FILE descriptors are enabled) */
if (Handle != 0)
{
return -1;
}
for (/*Empty*/; BufSize > 0; --BufSize)
{
unsigned char c = KB_IO;
if (c == 0)
break;
*Buf++ = c;
++nChars;
}
return nChars;
}有人知道吗?
发布于 2012-12-21 09:57:29
这就是“编译器如何从我所写的代码中生成代码”,只有编译器编写人员才能为您回答这个问题。
显然,在__no_init上面的代码中有几个非标准的C组件,@的使用等等。在我阅读这个组件时,它告诉编译器“这是一个HW端口,它提供一个无符号字符,它的地址是0xd2”。编译器将生成正确的指令来读写这样的端口--确切地说,它的工作方式取决于编译器、编译器正在为之生成代码的处理器等等。
P10out结构定义了位字段,这是C标准的一部分。谷歌是你在这里的朋友。
发布于 2012-12-21 06:49:27
间接运算符(一元*)返回与指针地址处的值相等的l值.
#define LCDCW1_ADDR 0xc000
void f()
{
uint32_t a = *(volatile uint32_t *)LCDCW1_ADDR; //reading from LCDCW1_ADDR
*(volatile uint32_t *)LCDCW1_ADDR = 0xffff; //writing to LCDCW1_ADDR
/*...*/
}基本上,编译器足够聪明地看到,a = *addr;表达式的意思是“从addr地址读取值并将其放入a。”同时,*addr = 0xffff将被解释为"put 0 0xffff to addr address“。
在这种情况下,您可以在赋值操作符的左侧和右侧使用READ_LCDCW1()宏。不需要单独的WRITE_LCDCW1(val)宏。我们可以将前面的代码重写为:
#define LCDCW1_ADDR 0xc000
#define LCDCW1 (*(volatile uint32_t *)LCDCW1_ADDR)
void g()
{
uint32_t a = LCDCW1; //reading from LCDCW1_ADDR
LCDCW1 = 0xffff; //writing to LCDCW1_ADDR
/*...*/
}IAR中的P1OUT宏很可能与上面的LCDCW1定义相同(如果遵循DEFC()定义,最终会发现类似的东西)。
发布于 2013-02-24 07:36:49
我的问题是,当使用任何微控制器时,请考虑一个例子MSP430。
你不用任何微控制器,你用的是MSP430.它有内存映射的IO (对我们程序员来说使用起来真的很好)。内存映射将根据设备的不同而有所不同。任何与地址相关的问题的答案都在您特定设备的用户指南中。TI是非常好的用户指南。找到一个为您的具体设备,并彻底阅读。
我的问题是它是如何写到那个地址的,例如在0x0021这个例子中。IDE是IAR。
编译器粘合代码。编译器供应商将向您提供写入设备地址所需的标头、宏和函数。使用编译器供应商的代码,除非您能够绝对证明它不适用于您的情况(对于IAR,我假设99.9%的代码有效,您将得到所需的费用。可能有一个全新的设备在实现中存在缺陷,但除非您能够证明它,否则可能不会)。
还有,让我知道你在0x0021u是什么意思?
根据您发布的信息,这是端口1的基本地址,看起来您可以控制端口1上的8个引脚。
#pragma language=extended从现在开始,你必须假设会发生各种各样的“魔法”(也就是非标准C)的事情。您可以推断出您认为编译器在做什么(在大多数情况下,这是相当清楚的),但是这是定义的实现,这意味着只有IAR编译器支持接下来会发生的事情。查看编译器文档以获得特定的命令和含义。最值得注意的是,__no_init和@符号是不标准的。__no_init不会在C启动时(即main()运行之前)初始化变量。@看起来像是给链接器的绝对地址指令(我在这里可能错了)。
__no_init volatile union
{
unsigned char P1OUT; /* Port 1 Output */
struct
{
unsigned char P0 : 1; /* */
unsigned char P1 : 1; /* */
unsigned char P2 : 1; /* */
unsigned char P3 : 1; /* */
unsigned char P4 : 1; /* */
unsigned char P5 : 1; /* */
unsigned char P6 : 1; /* */
unsigned char P7 : 1; /* */
}P1OUT_bit;
} @0x0021;这定义了一种获取端口1字节的特定位的方法,这允许您操作IO引脚。有人会说OMG位域是可移植的,are实现是定义的!是的,他们是对的,但IAR是实现者,所以在这种情况下,只要相信他们会做正确的事情。
最后,您可能只想使用定义的IAR宏。你花了很多钱买它们(除非你用的是免费的启动版)。您可以专注于编写应用程序,而不是以这种方式操作比特。IAR在标准化它们的名称方面做得很好,所以您也可以在相关的部分上使用相同的代码(或非常类似的代码)。如果您切换到一个不同的编译器,所有这些都会跳出窗口,您将不得不以新编译器的方式来完成它。这种方法的优点和缺点,可能没有“正确”的答案。
https://stackoverflow.com/questions/13984214
复制相似问题