[url=]PSoC 1 GPIO Demystified - Part 2[/url]
By M Ganesh Raaja
In Part-1 we looked at the features of the PSoC1 GPIO. In this part, let us look at application scenarios.
WRITING TO A GPIO PIN
To control a GPIO using the CPU, first the GPIO pin should be set to StdCPU mode in the GPIO configuration window in device editor. This will clear the corresponding bit in the PRTxGS register disconnecting the pin from the Global bus. The drive mode should be set to any mode other than HighZ or HighZ Analog. Now the pin may be controlled by writing to the PRTxDR register. Following are some examples in assembly and C.
Assembly Examples:
or reg[PRT1DR], 0x02 ; Set P1[1]
and reg[PRT1DR], ~0x80 ; Clear P1[7]
xor reg[PRT0DR], 0x04 ; Toggle P0[2]
C Examples:
PRT1DR |= 0x02; // Set P1[1]
PRT1DR &= ~0x80; // Clear P1[7]
PRT0DR ^= 0x04; // Toggle P0[2]
Notes:
- M8C.inc file has the register definitions for assembly and m8c.h file has the register definitions for C.
- While writing in assembly make sure that the Register bank is set to 0 before writing to the PRTxDR register. Register bank may be set to 0 by using the M8C_SetBank0 macro
- While using C, the compiler takes care of register bank switching.
However, this method has a disadvantage that it is not portable. For example, let us say we have placed an LED on P0[5] and have used PRT0DR to control the LED. Later in the design cycle of the project, the LED is to be moved to P1[2]. Now we will have to search all the locations in the project where PRT0DR is accessed and change this to PRT1DR. Using Pin names will solve this problem. Below is the procedure.
1. Give a meaningful name to the pin to be controlled. For example “LED”
2. PSoC designer generates include files called psocgpioint.inc and psocgpioint.h, which has defines for the registers and masks for the pins.
3. Include psocgpioint.inc or psocgpioint.h file in the C or assembly source file. Now the pins can be controlled by using code like:
LED_Data_ADDR |= LED_MASK; // C Code
or reg[LED_Data_ADDR], LED_MASK ; Assembly code
Now, if the LED were to be moved to any other port, the register definition in the psocgpiont.inc and psocgpioint.h files will be automatically updated by the PSoC designer and there will be no need to change the application code.
READING A GPIO PIN
The state of a GPIO can be read by reading the PRTxDR register. One important thing to bear in mind is that the PRTxDR register reflects the state of the GPIO pin, not the value that was written to the register. For example, if you had written a 1 to a GPIO pin configured as a pull up and when reading the PRTxDR register, if the pin were shorted to GND externally, the value read from the PRTxDR will be 0, not 1. This behavior gives rise to some interesting but unwanted situations which are explained in the next section on Read Modify Write operations.
Following are C and Assembly code examples to read from a GPIO pin. Either the PRTxDR can be directly used or register and mask names from psocgpioint include files can be used. In the below examples, an input pin named SWITCH is read.
Assembly Example:
mov A, reg[SWITCH_Data_ADDR]
and A, SWITCH_MASK
jnz PinHigh
; Pin state is Low
PinHigh:
; Pin state is high.
C Example:
if (SWITCH_Data_ADDR & SWITCH_MASK)
{
// Pin is set. Add code to process
}
Notes:
- When a GPIO pin has to be configured as a digital input, set the drive mode to HighZ, not HighZ Analog. As the Schmitt trigger section in the GPIO cell is disabled in the HighZ Analog mode, the state of the PRTxDR register bit corresponding to the pin will always be zero.
- When a pin is configured for Pull Up mode, for example to read an external switch connected between the pin and GND, a 1 has to be written to the PRTxDR register bit to enable the pull up resistor. Similarly, when a GPIO is configured in Pull Down mode, a 0 has to be written to the PRTxDR register bit.
READ MODIFY WRITE INSTRUCTIONS AND SHADOW REGISTERS
When you have input pins configured as Pull Up or Pull Down, and if there are other pins on the same port which are configured as output pins, an instruction that is used to update an output pin may accidentally set or clear the input pin thus latching the input. For example let us consider the following scenario.
P1[0] - PullUP, Connected to a switch. 1 has been written to the bit in PRT1DR to enable the pullup
P1[1] - Strong, drives an LED.
Now, the following instruction is used to switch off the LED
and reg[PRT1DR],~0x02
The above instruction is a Read/Modify/Write instruction, ie, it first reads the pin states (not the value written to PRT1DR register before), does an “AND” operation with the mask and then writes back the result to PRT1DR register. If the switch were pressed when this instruction is executed, the pin state of P1[0] would be 0, so the result of the operation would be
Pin State: x x x x x x x 0
AND operation: (x x x x x x x 0) & (11111101) = x x x x x x 0 0
Write back: x x x x x x 0 0
Bit-0 which drives the switch has been cleared in PRT1DR now and will never become high. This will result in the input pin stuck to 0, irrespective of the state of the switch.
A workaround for this condition is using Port shadow registers. Declare a variable in RAM and initialize this variable in the beginning of code to the initial Port value. After this, do any Read/Modify/Write operation on this shadow register and then write the value of shadow register to the PRTxDR register.
// Declare a shadow register
BYTE Port1Shadow;
// Initialize in the beginning of code
// Bit0 is set to enable the Pullup for the key
Port1Shadow = 0x01;
PRT1DR = Port1Shadow;
// Now to clear the LED
Port1Shadow &= ~0x02;
PRT1DR = Port1Shadow;
WRITING A GPIO ISR
Now, let us configure a GPIO pin for interrupt and write an ISR that increments a variable “IntCount”. In the Device Editor GPIO configuration, set the interrupt to the desired type. The PSoC Designer will take care of the PRTxIE and PRTxICx registers in the boot.asm code
In main, enable the Global interrupt by using the M8C_EnableGInt macro
In main,enable the GPIO interrupt by using the below code.
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO); // C code to enable GPIO interrupt
M8C_EnableIntMasm INT_MSK0, INT_MSK0_GPIO ; Assembly code
Whenever a pin is enabled for interrupt in the PSoC Designer Device editor, the IDE generates a file called psocgpioint.asm. This file has the place holder to write an ISR for the GPIO interrupt. The following code should be added within the user code marker in the ISR function.
PSoC_GPIO_ISR:
;@PSoC_UserCode_BODY@ (Do not change this line.)
;---------------------------------------------------
; Insert your custom code below this banner
;---------------------------------------------------
inc [IntCount]
;---------------------------------------------------
; Insert your custom code above this banner
;---------------------------------------------------
;@PSoC_UserCode_END@ (Do not change this line.)
reti
If you would like to write the ISR in C, then do the following.
Declare the ISR function using #pragma interrupt_handler
#pragma interrupt_handler MyGpioISR
Write the interrupt handler function.
void MyGpioISR(void)
{
IntCount++;
}
In the psocgpioint.asm file, place an ljmp instruction to branch to the C ISR
PSoC_GPIO_ISR:
;@PSoC_UserCode_BODY@ (Do not change this line.)
;---------------------------------------------------
; Insert your custom code below this banner
;---------------------------------------------------
ljmp _MyGpioISR
;---------------------------------------------------
; Insert your custom code above this banner
;---------------------------------------------------
;@PSoC_UserCode_END@ (Do not change this line.)
reti |