假设我们想要写地址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中找到了以下定义:
#define P1OUT_ (0x0021u) /* Port 1 Output */
DEFC( P1OUT , P1OUT_)
我认为它是定义地址,但其他宏要写入或读取。
有人可以解释一下P1OUT如何在该特定地址位置写入的流程吗?另请告诉我你在0x0021u中的含义是什么?
谢谢
到目前为止,我发现的细节是:
在msp430g2553.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_)
io430g2553.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 / 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;
}
有人知道吗?
答案 0 :(得分:4)
这是“编译器如何根据我编写的代码生成代码”,只有编译器编写者才能真正为您解答。
显然,__ no_init上面的代码中有几个非标准的C组件,使用@等等。在我的阅读中,它告诉编译器“这是一个提供无符号字符的HW端口,并且它的地址是0xd2“。编译器将生成正确类型的指令来读取和写入这样的端口 - 具体如何工作取决于编译器,编译器为其生成代码的处理器等。
P10out结构定义了位域,这是C标准的一部分。谷歌是你的朋友。
答案 1 :(得分:1)
间接运算符(一元*
)返回与指针地址处的值等效的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
将addr
被解释为“把0xffff放到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
/*...*/
}
宏。我们可以将以前的代码重写为:
P1OUT
来自IAR的 LCDCW1
宏很可能与上面的DEFC()
定义相同(如果您遵循{{1}}定义,您最终会找到类似的内容。)
答案 2 :(得分:0)
我的问题是,在使用任何微控制器时,请考虑使用 例如MSP430
你没有使用任何微控制器,你使用的是MSP430。它有内存映射IO(对我们程序员来说非常好用)。内存映射将根据设备而有所不同。任何与地址相关的问题的答案都在您的特定设备的用户指南中。 TI提供了非常好的用户指南。找到适合您特定设备的设备,并仔细阅读。
我的问题是它如何写入该地址,例如在这种情况下 0×0021。 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位域是可移植的,是实现定义的!是的,他们是对的,但IAR是实施者,所以在这种情况下,只要相信他们做正确的事。
最后请注意,您可能只想使用定义的IAR宏。你为他们付了很多钱(除非你使用免费的kickstart版本)。您可以专注于编写应用程序而不是以这种方式操作位。 IAR确实可以很好地标准化它们的名称,因此您也可以在相关部分使用相同的代码(或非常相似)。如果切换到不同的编译器,所有这些都会从窗口中消失,你必须按照新编译器的方式进行。这种方法有好有坏,可能没有“正确”的答案。