You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TM4C123GH6PM的GPIODATA寄存器异常初始化及正确读写方法咨询

TM4C123GH6PM GPIODATA寄存器异常问题解答

背景信息

我正在学习嵌入式系统课程,使用TM4C123GH6PM微控制器与Keil uVision IDE。编写的程序功能为:通过PF2控制板载LED常亮,当按下PF4连接的Switch1时LED闪烁,释放后恢复常亮。

代码实现

// BranchingFunctionsDelays.c Lab 6
// Runs on LM4F120/TM4C123
// Use simple programming structures in C to 
// toggle an LED while a button is pressed and 
// turn the LED on when the button is released.  
// This lab will use the hardware already built into the LaunchPad.
// Daniel Valvano, Jonathan Valvano
// January 15, 2016

// built-in connection: PF0 connected to negative logic momentary switch, SW2
// built-in connection: PF1 connected to red LED
// built-in connection: PF2 connected to blue LED
// built-in connection: PF3 connected to green LED
// built-in connection: PF4 connected to negative logic momentary switch, SW1

#include "TExaS.h"

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))
#define GPIO_PORTF_DIR_R        (*((volatile unsigned long *)0x40025400))
#define GPIO_PORTF_AFSEL_R      (*((volatile unsigned long *)0x40025420))
#define GPIO_PORTF_PUR_R        (*((volatile unsigned long *)0x40025510))
#define GPIO_PORTF_DEN_R        (*((volatile unsigned long *)0x4002551C))
#define GPIO_PORTF_AMSEL_R      (*((volatile unsigned long *)0x40025528))
#define GPIO_PORTF_PCTL_R       (*((volatile unsigned long *)0x4002552C))
#define SYSCTL_RCGC2_R          (*((volatile unsigned long *)0x400FE108))
#define SYSCTL_RCGC2_GPIOF      0x00000020  // port F Clock Gating Control
    
// basic functions defined at end of startup.s
void DisableInterrupts(void); // Disable interrupts 
void EnableInterrupts(void);  // Enable interrupts
void Delay100ms(unsigned long time); 
void init(void);

int main(void){
  TExaS_Init(SW_PIN_PF4, LED_PIN_PF2);  // activate grader and set system clock to 80 MHz
  init();// initialization goes here
    EnableInterrupts();           // enable interrupts for the grader
   while(1){
    unsigned long in;
        Delay100ms(1);
        in = GPIO_PORTF_DATA_R & 0x10; //read switch
        if(in == 0x00){//if PF4 == 0 (switch is pressed)
            GPIO_PORTF_DATA_R ^= 0x04; //toggle PF2
        }
        else{//if PF4 == 1 (switch not pressed)
            GPIO_PORTF_DATA_R = 0x04; //set PF2 so LED is ON
        }
  }
}

void init(void){
    SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF; //turn on clock for Port F
    Delay100ms(1);
    GPIO_PORTF_AMSEL_R = 0x00; //clear PF4 and PF2 bits in port F AMSEL to disable analog
    GPIO_PORTF_PCTL_R = 0x00; //clear PF4 and PF2 bit fields in Portf PCTL to config as GPIO
    GPIO_PORTF_DIR_R = 0x04; //Set port F dir reg so PF4 is in and PF2 is out
    GPIO_PORTF_AFSEL_R = 0x00; //clear PF4 and PF2 bits in port F AFSEL to disable alt func
    GPIO_PORTF_DEN_R = 0x14; //set PF4 and PF2 bits in Port F DEN to enable digital
    GPIO_PORTF_PUR_R = 0x10; //set PF4 bit in Port F PUR to activate internal pullup
    GPIO_PORTF_DATA_R = 0x04; //set PF2 bit in Port F DATA so the LED is init on
    PF2 = 0x20;
}

void Delay100ms(unsigned long time){
    unsigned long i = 1333333;
     while(time > 0){
        while(i > 0){
            i--;
        }
        time--;
    }

}

问题描述

程序执行到init()函数中,完成SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;和Delay100ms(1);后,GPIO_PORTF_DATA_R寄存器被初始化为0x011;当尝试设置GPIO_PORTF_DATA_R = 0x04时,寄存器值变为0x15。查阅数据手册发现GPIODATA寄存器的读写方式较为特殊,请求解释该异常现象,并告知正确的读写方法。

GPIODATA异常初始化行为


问题分析与解决方法

异常原因

TM4C系列的GPIODATA寄存器采用**位带别名(Bit-Banding Aliasing)**机制,直接读写PORTF的DATA基地址0x400253FC时,实际行为会受到引脚配置的约束:

  1. 初始化仅使能了PF2和PF4的数字功能(GPIO_PORTF_DEN_R = 0x14),PF0、PF1、PF3未配置,处于不确定的浮空/模拟状态,读取DATA寄存器时会包含这些引脚的无效值;
  2. 直接赋值GPIO_PORTF_DATA_R = 0x04时,位带机制仅会修改已被GPIODEN使能的引脚位,未使能的引脚位状态会被保留。你看到的0x15是:PF4(0x10)的上拉高电平+PF2(0x04)的设置值+PF0(0x01)的默认高电平,三者叠加的结果。

正确读写方法

针对TM4C的GPIO_DATA寄存器,推荐以下两种安全操作方式:

  1. 位掩码精准操作目标引脚
    通过位运算仅修改需要控制的引脚位,避免影响其他未配置引脚:

    • 设置PF2为高电平:GPIO_PORTF_DATA_R |= 0x04;
    • 清除PF2电平:GPIO_PORTF_DATA_R &= ~0x04;
    • 翻转PF2状态:GPIO_PORTF_DATA_R ^= 0x04;
  2. 使用位带别名地址
    TM4C提供了位带别名区域,每个引脚对应独立的地址,直接读写该地址可精准控制单个引脚,完全不影响其他引脚。例如PF2的位带别名地址可定义为:

    #define PF2 (*((volatile unsigned long *)0x40025010))
    

    之后直接操作PF2 = 1;或PF2 ^= 1;即可单独控制PF2。

  3. 额外优化建议

    • 初始化时禁用未使用引脚的数字功能,避免引入不确定状态:
      GPIO_PORTF_DIR_R &= ~0x1B; // 将PF0、PF1、PF3设为输入
      GPIO_PORTF_DEN_R &= ~0x1B; // 禁用这些引脚的数字功能
      
    • 删除init()函数中最后一行错误的PF2 = 0x20;,0x20对应PF5而非PF2,属于无效操作。

内容的提问来源于stack exchange,提问作者Eric Kemmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 11:55:18