更改寄存器值时,能否将微控制器不同端口的各个引脚映射到寄存器并更改其值?


12

问:能否将微控制器不同端口的各个引脚映射到寄存器,并在更改寄存器值时更改其值?

场景:我已经用完了微控制器每个端口(8位)的一些引脚。现在,我想连接一个需要8位总线的设备(假设D0至D7 IN SEQUENCE),也就是说我需要来自控制器的8个引脚,以便可以一对一的方式连接它们

portx0  -> D0 // x is the name of port followed by bit location on that port
portx1  -> D1
...
portx7  -> D7

但是我没有可以与该设备连接的8个引脚的整个端口,而是从portx,Porty和Portz提供了一些引脚。新的连接方案为(分别从微控制器到设备的连接)

portx0  -> D0
portx1  -> D1
portx2  -> D2
porty4  -> D3
porty5  -> D4
porty6  -> D5
porty7  -> D6
portz1  -> D7

在这种情况下,如果我要发送值,请说

unsigned char dataReg = 0xFA;

从控制器到我的设备,我必须对要发送的值执行逐位运算,并根据寄存器中的值分别设置每个引脚。例如

portx0 = ((dataReg & 0x01) >> 0 );  // Masking and shifting as bit position
portx1 = ((dataReg & 0x02) >> 1 );
portx2 = ((dataReg & 0x04) >> 2 );
porty4 = ((dataReg & 0x08) >> 3 );
porty5 = ((dataReg & 0x10) >> 4 );
porty6 = ((dataReg & 0x20) >> 5 );
porty7 = ((dataReg & 0x40) >> 6 );
portz1 = ((dataReg & 0x80) >> 7 );

现在,成为主要问题,为避免在不同端口的每个位上进行这些单独的计算,微控制器不同端口的各个引脚能否映射到寄存器,并在更改寄存器值时更改其值?


1
前段时间我有同样的想法。使用PIC,这是不可能的:microchip.com/forums/tm.aspx? high=&m=696277- 我认为任何微型芯片都不可能,但是列出您的设备会有所帮助。

Answers:


6

看来您的问题归结为固件中具有8位值,并且想从任意端口引脚中读取和写入该值。

没有直接的硬件方法可以做到这一点。您必须编写两个例程,一个例程读取8位值,另一个例程将其写入。其他人提到使用联合,但这不是一个好主意。使用联合,您必须分别处理每个位,并且代码变得取决于微控制器的位顺序。如果所有8位都完全独立地分散,这可能是无论如何要采用的方法。如果是这样,您几乎无能为力,只能为每个位制作特殊的代码。

做到这一点的更好方法,尤其是如果您可以在物理端口上的几个连续块中将位分组时,可以使用屏蔽,移位和或。例如,如果内部字节的低三位位于端口的位<6-4>上,则将该端口值右移4,然后将其与7进行“与”运算,以使这些位进入最终位置。将其他端口的位移位和屏蔽(或掩码和移位)到位,并通过对结果进行或运算来组装最后的8位字节。

这种低级位纠错比汇编程序更容易在C语言中完成。我可能会将字节读写例程放在单个汇编程序模块中,并使接口可从C调用。


6
我的答案与您的答案几乎相同,只是我完全不使用汇编。在C中,位操作是微不足道的。我认为(重新)学习编译器的特定C调用约定以及如何运行链接器会更让人头疼。真正取决于编译器以及它使事情变得多么困难。:-)
akohlsmith

@安德鲁:认真吗?我见过的任何编译器手册中都有可能需要与汇编代码进行接口连接,清楚地阐明了调用约定。用C语言写操作时,位操作可能是“琐碎的”,但这是编译器可以产生可怕代码的领域。如果速度或代码空间无关紧要,请使用您更喜欢的方式。我更喜欢汇编器来进行低级位调整,所以我会使用它。如果这是低级速度关键例程,则应在汇编器中执行。确实应该很容易。
Olin Lathrop

1
我要说的是,除非有充分的理由,否则我不愿意为琐碎的操作而进行处理。我们不知道他的并行总线的细节,但是大多数总线具有选通信号,从而消除了对所有总线引脚的“近原子”更新的需要,因此降级组装可能是不必要的优化和不必要的复杂性(即使它是直率的)。
akohlsmith

@Andrew:如果您不知道自己在做什么,那只会变得混乱或复杂。我认为真正的问题是某些人害怕汇编器,并且不太了解。错了 它必须是工具箱中的现成工具。如果您对它不太了解或不满意,那么您将始终以其他方式证明自己应该如何做。如果您对汇编程序和HLL都了解的话,有些事情在汇编程序中会更容易。大多数人没有,但这是他们的问题,而不是使用汇编程序。
Olin Lathrop

2
我精通许多微控制器/微处理器上的汇编语言。我不同意它应该是现成的工具。应该仅在必要时才谨慎使用它,通常用于非常低级的初始化,对时序或大小有严格要求的代码,或者在更常见的情况下,对已经确定为瓶颈的区域进行优化。我发现在那些因为汇编程序在那里而跳到汇编程序的作者经常写不太清晰的代码,或者在算法使用不当时无法识别。我并不是在说那是您,而是在更一般的情况下。
akohlsmith

4

通常这是不可能的。据我所知,PIC是不可能的。

我知道只有一个微控制器可以实现此功能,即Cypress PSoC。这是一个高度可配置的片上系统。它允许您执行的许多操作中,实际上是定义您自己的寄存器(1-8位)并将其连接到您喜欢的任何引脚,甚至是内部电路。

PSoC接线

例如,在这里我创建了一个6位控制寄存器。其中5位直接进入引脚,而我正在使用的第6位与第7位输入进行异或。

PSoC引脚

在芯片上,我可以选择将这些引脚分配给任何可用的GPIO引脚。(是灰色的一个图像)


1
LPC800还应该能够执行此操作,因为可以将功能自由分配给引脚。
starblue 2013年

-1

您可以尝试以下方法。编写一个自己的结构,该结构映射到两个端口的相应引脚(将要使用),现在更新该寄存器中的值将设置/复位这两个端口的引脚。只要尝试,让我们知道它是否有效!

我相信这应该可行。


2
在C语言中,您可以将结构映射到内存位置,也可以将结构的位(位字段)映射到位偏移量,但是无法防止编译器弄乱“中间”位,现在有了查看“整体”结构的单个整数值的方法。这行不通。
Wouter van Ooijen

-1

如果我正确理解了这个问题,那么在C语言中就很容易了:

通用类型声明,可重复用于任何寄存器:

typedef union    // Generic 8-bit register Type
{
  uint8 reg; // Whole register
  struct
  {
    unsigned  bit7     : 1;  // Bit 7 
    unsigned  bit6     : 1;  // Bit 6 
    unsigned  bit5     : 1;  // Bit 5 
    unsigned  bit4     : 1;  // Bit 4 
    unsigned  bit3     : 1;  // Bit 3 
    unsigned  bit2     : 1;  // Bit 2 
    unsigned  bit1     : 1;  // Bit 1 
    unsigned  bit0     : 1;  // Bit 0 
  } bit;
} typ_GENERIC_REG8;

因此,要定义我们要处理的端口:

#define MCU_GPO_PORTx   (*(volatile typ_GENERIC_REG8 *)(0x12345678)) // Number is address

并直接在该端口上旋转一个引脚:

#define MCU_PORTx_PINn  (MCU_GPO_PORTx.bit.bit0)

在代码中:

MCU_PORTx_PINn = 1; // Set pin high

整个注册:

MCU_GPO_PORTx.reg = 0xF; // All pins high

非常值得阅读有关struct,union,typedef和enum的信息-所有这些使嵌入式和一般的生活变得更加美好!


OP希望将来自不同端口的几个位组合成一个字节。我不知道这样做会如何?Olin Lathrop 解释了为什么不可能做到这一点。

这实际上并不能解决问题,并且取决于编译器的“ smrt”程度,可能会产生一整套调试新问题。
akohlsmith
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.