Search code examples
embeddedmicrocontrollergpiomemory-mapped-io

TM4321GH6PM - why is GPIODATA register initializing itself and how to read and write to it properly?


Context: I am following a Embedded Systems course that uses the TM4C321GHP6M microcontroller. The IDE being used is the uvision ide by keil. The purpose of the program I am running is to turn on an on-board LED using PF2 and when Switch 1, connected via PF4, is pressed the led will blink. Once switch 1 is released it LED will go back to just being ON.

// 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--;
    }

}

For some reason once the program goes to the init() function and steps over SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF; and then Delay100ms(1); the register GPIO_PORTF_DATA_R is initialized with value 0x011. I am not sure what is causing this. Additionally when I attempt to set GPIO_PORTF_DATA_R = 0x04 it then holds the value 0x15? This behavior is very strange and after reading through the data sheet I can see that GPIODATA is read and written to in a unconventional way. For reference please see pages 662 and 654. Would anyone be able to explain this behavior and how I could properly read and write to this register? GPIODATA strange initialization behavior


Solution

  • This line:

    SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF; //turn on clock for Port F
    

    enables the GPIOF clock. When the clock for a peripheral is not running its registers cannot be read or written. So in the debugger you do not see the register value until after the clock is enabled.

    Although the reset state of GPIODATA is documented as 0x00000000, that is only true for output the default state of GPIODIR sets every GPIO pin to an input. So 0x11 simply reflects the input state of that port, and PF0 and PF4 happen to be in the logic-high state.

    You would need to consult the board schematic to determine what is connected to those pins and why they might be in the high state, but you have already mentioned that PF4 is connected to SW1, and the code sets that pin as an input with internal pull-up. I am guessing that this is a Tiva LaunchPad board having: enter image description here Pressing the SW1 push-button will pull the pin low, and the PF4 pit will become zero. The internal pull-up resistor is enabled by default for PF4, so it is not floating even though you have not yet configured it at that point. PF0 GPIOPUR default is floating (per table 10-8), so unless it is configured explicitly to pull-up, its state is indeterminate when connected as on the LaunchPad to SW2.

    With respect to perceived "unconventional" behaviour, all peripheral registers on any MCU behave as defined by their manufacturer documentation according to the hardware design. Hardware registers are not RAM (even when memory-mapped) and need not behave like RAM. In particular, unlike RAM, they may have deterministic reset state, and be either read-write, read-only, or write-only. Writing a bit need not modify that bit, registers may change value independently of reads or writes by code.