在ARM中使用32位数据类型和8位数据类型

时间:2015-03-28 08:09:58

标签: c arm embedded

我是ARM LPC2148微控制器的新手,也是StackOverflow的新手。我刚刚在其中一个评估板上看到了一段代码。我正在粘贴,因为它在下面。

端口引脚P0.19至P0.22映射到LCD的D4至D7。以下功能用于向以4位模式操作的LCD发送命令:

void LCD_Command(unsigned int data)     // This function is used to send LCD commands
{
 unsigned int temp=0;
 EN_LOW();                      // Set EN pin of LCD to to Low
 COMMAND_PORT();
 WRITE_DATA();

 temp=data;
 IO0PIN&=0xFF87FFFF;
 IO0PIN|=(temp & 0xF0) << 15;

 EN_HI();   // Give strobe by enabling and disabling En pin of LCD
 EN_LOW();

 temp=data & 0x0F;
 IO0PIN&=0xFF87FFFF;
 IO0PIN|=(temp) << 19;

 EN_HI();
 EN_LOW();
 while(Busy_Wait());
 Delay(10);
} 

我的问题是:

  1. 变量&#34;数据&#34;已经是32位宽。以这种方式转移数据是否有效?编码器可以通过32位数据然后屏蔽(&amp;)/ ORed(|)。或者还有其他影响吗?

  2. 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?由于寄存器是32位宽,我不确定是否在内部进行了任何分段以节省内存。

  3. 有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一?在上面的代码中,通过硬编码(<<15<<19等)完成移位。我们可以避免这种硬编码并使用一些#defines来映射这些位吗?

3 个答案:

答案 0 :(得分:1)

  
      
  1. 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?
  2.   

仅在将它们存储到RAM中时,一旦优化器打开,这个小功能将无法执行。请注意,使用char类型可能会引入其他代码,以便正确处理溢出。

  1. [...]我们可以避免这种硬编码并使用一些#defines来映射这些位吗?
  2. 易:

    #define LCD_SHIFT_BITS 19
    
    void LCD_Command(unsigned int data)     // This function is used to send LCD commands
    {
     unsigned int temp=0;
     EN_LOW();                      // Set EN pin of LCD to to Low
     COMMAND_PORT();
     WRITE_DATA();
    
     temp=data;
     IO0CLR = 0x0F << LCD_SHIFT_BITS;
     IO0SET = (temp & 0xF0) << (LCD_SHIFT_BITS - 4);
    
     EN_HI();   // Give strobe by enabling and disabling En pin of LCD
     EN_LOW();
    
     temp=data & 0x0F;
     IO0CLR = 0x0F << LCD_SHIFT_BITS;
     IO0SET = temp << LCD_SHIFT_BITS;
    
     EN_HI();
     EN_LOW();
     while(Busy_Wait());
     Delay(10);
    } 
    

    我也更改了针脚设置并清除为原子。

答案 1 :(得分:0)

  
      
  1. 变量&#34;数据&#34;已经是32位宽。以这种方式转移数据是否有效?编码器可以通过32位数据然后屏蔽(&amp;)/ ORed(|)。或者还有其他影响吗?

  2.   
  3. 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?由于寄存器是32位宽,我不确定是否在内部进行了任何分段以节省内存。

  4.   

由于您使用的是32位MCU,因此减小可变大小不会使代码更快。它可能会使它变慢,即使你也可以通过这种方式节省几个字节的RAM。

然而,这些都是您不应该关注的微观优化。启用优化并将它们留给编译器。如果由于某种原因,未知的必须微观优化您的代码,那么您可以使用uint_fast8_t代替。它是一个至少为8位的类型,编译器将选择最快的类型。

在32位CPU上尽可能多地使用32位整数通常是一个好主意,以避免C语言中各种复杂的隐式类型提升规则引起的众多细微错误。特别是在嵌入式系统中,整数提升和类型平衡因引起许多微妙的错误而臭名昭着。 (MISRA-C检查员可以帮助防范这种情况。)

  
      
  1. 有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一?在上面的代码中,通过硬编码(&lt;&lt; 15或&lt;&lt; 19等)完成移位。我们可以避免这种硬编码并使用一些#defines来映射这些位吗?
  2.   

一般来说,你应该避免&#34;魔术数字&#34;等等。不是出于性能原因,而是出于可读性。

最简单的方法是使用预制的寄存器映射表,如果你有一个编译器。如果没有,您必须手动#define注册:

#define REGISTER (*(volatile uint32_t*)0x12345678)
#define REGISTER_SOMETHING 0x00FF0000 // some part of the register

然后定义所有可能的值,例如

#define REGISTER_SOMETHING_X 0x00010000
#define REGISTER_SOMETHING_Y 0x00020000
...

REGISTER = REGISTER_SOMETHING & REGISTER_SOMETHING_X;
// or just:
REGISTER |= REGISTER_SOMETHING_X;

REGISTER = REGISTER_SOMETHING_X | REGISTER_SOMETHING_Y;
// and so on

或者,如果寄存器的一部分是可变的:

#define REGISTER_SOMETHING_VAL(val) \
  ( REGISTER_SOMETHING & ((uint32_t)val << 16) )
...
REGISTER = REGISTER_SOMETHING_VAL(5);

您可以通过多种方式编写此类宏以及使用它们的代码。专注于打开调用代码可读,没有&#34;魔术数字&#34;。对于更复杂的东西,请考虑使用内联函数而不是类似函数的宏。

对于嵌入式系统,如果所有寄存器部分都是通过一次访问写入,请考虑它是否有任何区别。在某些情况下,如果您不这样做,可能会遇到严重错误,具体取决于特定寄存器的性质。清除中断掩码等时需要特别小心。最好总是反汇编这些代码并查看最终的机器代码。


一般建议:

始终考虑结束,对齐和可移植性。您可能认为您的代码永远不会被移植,但可移植性可能意味着在其他项目中重用您自己的代码。

如果对任何形式的硬件或数据传输协议映射使用结构/联合,则必须使用static_assert以确保没有填充或其他对齐技巧。 Do not use struct bit-fields在任何情况下都可以!由于种种原因,它们很糟糕,无法在任何形式的程序中可靠地使用,尤其是嵌入式微控制器应用程序。

答案 2 :(得分:-1)

三个问题,许多编程风格。

此代码绝对是错误代码。没有原子访问权限......请帮助自己,不要将其作为参考。

  
      
  1. 变量“data”已经是32位宽。它有效吗......
  2.   

没有其他影响。程序员在函数内部使用了额外的4byte局部变量。

  
      
  1. 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?
  2.   

通常,您只能在RAM中保存内存。大多数链接脚本将数据对齐为4或8个字节。当然,您可以使用结构来绕过RAM和Flash。如需考虑:

// ...
struct lala {
   unsigned int  a  :12;
   unsigned int  b  :20;
   long          c;
   unsigned char d;
};
const struct lala l1;   // l1 is const so it lives in Flash.
// Also l1.d is 8byte long ;)
// ...

最后一个问题将我们带到问题3。

  
      
  1. 有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一? ...
  2.   

恩智浦的LPC2000是一个小端CPU see here for details。这意味着您可以以成员适合您要访问的内存位置的方式创建结构。要实现这一点,您必须先放置低内存地址。例如:

// file.h
// ...
#include <stdint.h>
typedef volatile union {
   struct {
      uint8_t  p0  :1;
      uint8_t  p1  :1;
      uint8_t  p2  :1;
      uint8_t  p3  :1;
      ...
      uint8_t  p30 :1;
      uint8_t  p31 :1;
   }pin;
   uint32_t port;
}port_io0clr_t;
// You have to check it make sure

// Now we can "put" it in memory.
#define REG_IO0CLR    ((port_io0clr_t *) 0xE002800C)
   //!< This is the memory address of IO0CLR in address space of LPC21xx

现在我们可以使用REG_IO0CLR指针。例如:

// file.c
// ...
int main (void) {
   // ...
   REG_IO0CLR->port = 0x0080;   // Clear pin P0.7
   // or even better
   REG_IO0CLR->pin.p4 = 1;      // Clear pin p0.4
   // ...

return 0;
}