【问题标题】:Embedded C: Registers Access嵌入式 C:寄存器访问
【发布时间】:2012-12-08 16:24:59
【问题描述】:

假设我们想写地址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;
}

有人知道吗?

【问题讨论】:

  • "DEFC()" 几乎可以肯定是一个宏,就像 "READ_LCDCW1()" 和 "#define LCDCW1_ADDR 0xc000"" 是宏一样。您必须查看系统上的 *.h 头文件之一才能了解它的作用。 “u”仅表示数字“0x0021”是“无符号”。您可以放心地将其关闭。
  • 如果你有一个特定的地址并且你想在/从那个位置写/读,你使用一个指针......
  • #define DEFC(name, address) __no_init volatile unsigned char name @address; #define DEFW(name, address) __no_init volatile unsigned short name @address;在 msp430g2553.h 中找到上述详细信息

标签: c compiler-construction embedded msp430 iar


【解决方案1】:

我的问题是,在使用任何微控制器时,请考虑 示例 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 位域是可移植的,是实现定义的!是的,他们是对的,但 IAR 是实施者,所以在这种情况下,请相信他们会做正确的事。

最后请注意,您可能只想使用定义的 IAR 宏。您为他们支付了很多钱(除非您使用的是免费的 kickstart 版本)。您可以专注于编写您的应用程序,而不是通过这种方式操作位。 IAR 确实很好地标准化了它们的名称,因此您也可以在相关部分上使用相同的代码(或非常相似的代码)。如果您切换到不同的编译器,所有这些都会消失,您必须按照新编译器的方式进行操作。这种方法的优点和缺点,可能没有“正确”的答案。

【讨论】:

    【解决方案2】:

    这是“编译器如何根据我编写的代码生成代码”,只有编译器编写者才能真正为您解答。

    很明显,上面的代码中有几个非标准的C组件__no_init,@的使用等。在我的阅读中,它告诉编译器“这是一个硬件端口,提供无符号字符,并且它的地址是 0xd2"。编译器将生成正确类型的指令来读取和写入这样的端口 - 具体如何工作取决于编译器、编译器为其生成代码的处理器等。

    P10out 结构定义位域,它是 C 标准的一部分。谷歌是你的朋友。

    【讨论】:

      【解决方案3】:

      间接运算符(一元*)返回与指针地址处的值等效的左值。

      #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 将被解释为“将0xffff 放入@ 987654327@地址"

      在您的情况下,您可以在赋值运算符的左侧和右侧使用 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() 的定义,您最终会找到类似的东西)。

      【讨论】:

      • DEFC 可能是一些非标准的粘性物,使变量以#defined 名称出现在调试器中。
      • @Pavel .. 感谢您的回答.. 但我发布了这个问题是为了确切了解 IAR 编译器是如何进行和执行的......以及在哪里可以找到各种定义的详细信息......如果有人具有编写编译器代码的专业知识或 IAR 专业知识... :)
      猜你喜欢
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-23
      相关资源
      最近更新 更多