【问题标题】:Addressing pins of Register in microcontrollers微控制器中寄存器的寻址引脚
【发布时间】:2020-09-12 15:20:27
【问题描述】:

我正在开发 Keil 软件并使用 LM3S316 微控制器。通常我们在微控制器中寻址寄存器的形式是:

#define GPIO_PORTC_DATA_R       (*((volatile uint32_t *)0x400063FC))

我的问题是,例如,如果我有这种方法,我该如何访问寄存器的单个引脚:

char process_key(int a)
{  PC_0 = a ;}

如何获取 PC_0 以及如何定义它?

谢谢

【问题讨论】:

  • 屏蔽和移位。取决于微控制器,有些方法可以在没有读取-修改-写入的情况下设置或清除寄存器中的位。
  • cortex-m 有一个你读过的位带功能。
  • @old_timer 我查看了data sheet,LM3S316 的 GPIO 映射中似乎没有位设置/位复位寄存器。
  • 是的,我也这样做了,对于那部分来说,要么使用数据寄存器读取-修改-写入,要么使用位带(如果有)。我们没有听说要阅读 OP 的手册,而不是 SO 的目的。

标签: embedded microcontroller keil addressing


【解决方案1】:

假设说:

#define PIN0 (1u<<0)
#define PIN1 (1u<<1)
#define PIN2 (1u<<2)
// etc...

然后:

char process_key(int a)
{  
    if( a != 0 )
    {
        // Set bit
        GPIO_PORTC_DATA_R |= PIN0 ;
    }
    else
    {
        // Clear bit
        GPIO_PORTC_DATA_R &= ~PIN0 ;
    }
}

How do you set, clear, and toggle a single bit? 上介绍了这种惯用技术的概括

但是,如果寄存器可能在不同的线程/中断上下文中被访问,那么|= / &amp;= 所暗示的读取-修改-写入可能会出现问题,并且可能会增加不必要的开销。 Cortex-M3/4 部件具有称为位带的功能,允许直接和原子地寻址各个位。给定:

volatile uint32_t* getBitBandAddress( volatile const void* address, int bit )
{
    __IO uint32_t* bit_address = 0;
    uint32_t addr = reinterpret_cast<uint32_t>(address);

    // This bit maniplation makes the function valid for RAM
    // and Peripheral bitband regions
    uint32_t word_band_base = addr & 0xf0000000u;
    uint32_t bit_band_base = word_band_base | 0x02000000u;
    uint32_t offset = addr - word_band_base;

    // Calculate bit band address
    bit_address = reinterpret_cast<__IO uint32_t*>(bit_band_base + (offset * 32u) + (static_cast<uint32_t>(bit) * 4u));

    return bit_address ;
} 

那么你可以:

char process_key(int a)
{  
    static volatile uint32_t* PC0_BB_ADDR = getBitBandAddress( &GPIO_PORTC_DATA_R, 0 ) ;

    *PC0_BB_ADDR = a ;
}

您当然可以确定并硬编码位带地址;例如:

#define PC0 (*((volatile uint32_t *)0x420C7F88u))

然后:

char process_key(int a)
{  
    PC0 = a ;
}

位带地址计算的详细信息可以找到ARM Cortex-M Technical Reference Manual,还有一个在线计算器here

【讨论】:

  • 只是一个挑剔:确保始终写1u&lt;&lt;n。因为1&lt;&lt;31 在ARM 上调用未定义的行为,因为1 是有符号整数类型。同样,在所有十六进制常量上也使用u 后缀,否则它们最终可能会成为不同的类型。例如,0xf0000000 是 unsigned int 类型,而 0x02000000 是有符号 int 类型。如果运气不好,你可能会因为签名错误而导致一些微妙的错误。
  • @Lundin 如果你编辑了它,我一点也不生气——尽管你的工作当然不是修复我的错误。会修复。我从其他地方的旧答案(我的)复制并粘贴的位带地址代码。我很确定我目前使用的版本具有更好的类型协议,因为它已经通过静态分析 - 但当时我无法访问该版本。也缺少 PC0 def 上的 0x 前缀 - 也已修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
  • 2022-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多