首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >嵌入式C:寄存器访问

嵌入式C:寄存器访问
EN

Stack Overflow用户
提问于 2012-12-21 04:18:34
回答 3查看 5K关注 0票数 4

假设我们想在地址(比如0xc000 )上写,我们可以在C中定义一个宏为:

代码语言:javascript
运行
复制
#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中找到了以下定义:

代码语言:javascript
运行
复制
#define P1OUT_              (0x0021u)  /* Port 1 Output */
DEFC(   P1OUT             , P1OUT_)

我想它是在定义地址,但是其他要写或读的宏在哪里呢?

有谁能解释一下P1OUT在那个特定的地址位置是如何写的流吗?还有,让我知道你在0x0021u是什么意思?

谢谢

到目前为止,我发现的细节如下:

在msp430g2553.h 430g2553.h

代码语言:javascript
运行
复制
#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表示

代码语言:javascript
运行
复制
__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++编译器中找到的详细信息:

代码语言:javascript
运行
复制
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;
}

有人知道吗?

EN

回答 3

Stack Overflow用户

发布于 2012-12-21 09:57:29

这就是“编译器如何从我所写的代码中生成代码”,只有编译器编写人员才能为您回答这个问题。

显然,在__no_init上面的代码中有几个非标准的C组件,@的使用等等。在我阅读这个组件时,它告诉编译器“这是一个HW端口,它提供一个无符号字符,它的地址是0xd2”。编译器将生成正确的指令来读写这样的端口--确切地说,它的工作方式取决于编译器、编译器正在为之生成代码的处理器等等。

P10out结构定义了位字段,这是C标准的一部分。谷歌是你在这里的朋友。

票数 4
EN

Stack Overflow用户

发布于 2012-12-21 06:49:27

间接运算符(一元*)返回与指针地址处的值相等的l值.

代码语言:javascript
运行
复制
#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)宏。我们可以将前面的代码重写为:

代码语言:javascript
运行
复制
#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()定义,最终会发现类似的东西)。

票数 1
EN

Stack Overflow用户

发布于 2013-02-24 07:36:49

我的问题是,当使用任何微控制器时,请考虑一个例子MSP430。

你不用任何微控制器,你用的是MSP430.它有内存映射的IO (对我们程序员来说使用起来真的很好)。内存映射将根据设备的不同而有所不同。任何与地址相关的问题的答案都在您特定设备的用户指南中。TI是非常好的用户指南。找到一个为您的具体设备,并彻底阅读。

我的问题是它是如何写到那个地址的,例如在0x0021这个例子中。IDE是IAR。

编译器粘合代码。编译器供应商将向您提供写入设备地址所需的标头、宏和函数。使用编译器供应商的代码,除非您能够绝对证明它不适用于您的情况(对于IAR,我假设99.9%的代码有效,您将得到所需的费用。可能有一个全新的设备在实现中存在缺陷,但除非您能够证明它,否则可能不会)。

还有,让我知道你在0x0021u是什么意思?

根据您发布的信息,这是端口1的基本地址,看起来您可以控制端口1上的8个引脚。

代码语言:javascript
运行
复制
#pragma language=extended

从现在开始,你必须假设会发生各种各样的“魔法”(也就是非标准C)的事情。您可以推断出您认为编译器在做什么(在大多数情况下,这是相当清楚的),但是这是定义的实现,这意味着只有IAR编译器支持接下来会发生的事情。查看编译器文档以获得特定的命令和含义。最值得注意的是,__no_init和@符号是不标准的。__no_init不会在C启动时(即main()运行之前)初始化变量。@看起来像是给链接器的绝对地址指令(我在这里可能错了)。

代码语言:javascript
运行
复制
__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在标准化它们的名称方面做得很好,所以您也可以在相关的部分上使用相同的代码(或非常类似的代码)。如果您切换到一个不同的编译器,所有这些都会跳出窗口,您将不得不以新编译器的方式来完成它。这种方法的优点和缺点,可能没有“正确”的答案。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13984214

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档