我是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);
}
我的问题是:
变量&#34;数据&#34;已经是32位宽。以这种方式转移数据是否有效?编码器可以通过32位数据然后屏蔽(&amp;)/ ORed(|)。或者还有其他影响吗?
如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?由于寄存器是32位宽,我不确定是否在内部进行了任何分段以节省内存。
有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一?在上面的代码中,通过硬编码(<<15
或<<19
等)完成移位。我们可以避免这种硬编码并使用一些#defines来映射这些位吗?
答案 0 :(得分:1)
- 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?
醇>
仅在将它们存储到RAM中时,一旦优化器打开,这个小功能将无法执行。请注意,使用char
类型可能会引入其他代码,以便正确处理溢出。
易:
#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)
变量&#34;数据&#34;已经是32位宽。以这种方式转移数据是否有效?编码器可以通过32位数据然后屏蔽(&amp;)/ ORed(|)。或者还有其他影响吗?
- 醇>
如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?由于寄存器是32位宽,我不确定是否在内部进行了任何分段以节省内存。
由于您使用的是32位MCU,因此减小可变大小不会使代码更快。它可能会使它变慢,即使你也可以通过这种方式节省几个字节的RAM。
然而,这些都是您不应该关注的微观优化。启用优化并将它们留给编译器。如果由于某种原因,未知的必须微观优化您的代码,那么您可以使用uint_fast8_t
代替。它是一个至少为8位的类型,编译器将选择最快的类型。
在32位CPU上尽可能多地使用32位整数通常是一个好主意,以避免C语言中各种复杂的隐式类型提升规则引起的众多细微错误。特别是在嵌入式系统中,整数提升和类型平衡因引起许多微妙的错误而臭名昭着。 (MISRA-C检查员可以帮助防范这种情况。)
- 有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一?在上面的代码中,通过硬编码(&lt;&lt; 15或&lt;&lt; 19等)完成移位。我们可以避免这种硬编码并使用一些#defines来映射这些位吗?
醇>
一般来说,你应该避免&#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)
三个问题,许多编程风格。
此代码绝对是错误代码。没有原子访问权限......请帮助自己,不要将其作为参考。
- 变量“data”已经是32位宽。它有效吗......
醇>
没有其他影响。程序员在函数内部使用了额外的4byte局部变量。
- 如果我们使用unsigned char而不是unsigned int,我们是否在LPC21xx中保存任何内存?
醇>
通常,您只能在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。
- 有没有什么方法可以轻松地将8位数据映射到32位数据的8位部分之一? ...
醇>
恩智浦的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;
}