Blink
This guide will walkthrough the native compilation of a Blink executable for the phyCORE-i.MX7 Development Kit running Linux. This demo will blink an LED connected to UART3_TXD processor pad, which is accessible at pin 8 of the X11 “Pi Header” which is located on the PEB-D-RPI Expansion Board (with net name X_UART3_TX). In order for this demo to work properly we will also need to dive into configuring processor pins and the best reference for that is the Technical Reference Manual (TRM) and Datasheet for the i.MX7 processor, which can be downloaded from the NXP i.MX7 product page.
Note
This guide leverages build tools on the target (native compilation). These are not installed by default and must be first added to the target software. Checkout the Hello World guide for steps on adding this support.
Create the Blink Program
Let’s make a project directory to contain the Blink source code:
Target (Linux)
mkdir ~/blink-project
cd ~/blink-project
Create the main Blink application source code file using your favorite text editor:
Target (Linux)
vi blink.c
Note
The vi text editor begins in “Command Mode” and you must first hit the ‘i’ key in order to enter “Insert Mode”. Using the arrow keys to navigate, make the necessary changes and then hit ESC to go back to “Command mode”. Now enter “:wq” to write the file and quit.
Pro Tip: Use the right click on your mouse to paste! This will only work if you are in “Insert Mode” first.
Edit the contents of the file to reflect the following and remember to save your changes when you are done!
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
// Setup GPIO4_IO05
#define GPIO4_ADDR_START 0x30230000
#define GPIO4_ADDR_END 0x3023001C
#define GPIO4_SIZE (GPIO4_ADDR_END - GPIO4_ADDR_START)
#define GPIO4_PORT (1 << 5)
#define GPIO_DATA_OFFSET 0x000000000
#define GPIO_DIR_OFFSET 0x000000004
void *gpioAddress;
unsigned int *gpio_setdataout_addr;
unsigned int *gpio_direction_addr;
void delay(unsigned long ms)
{
clock_t start_ticks = clock();
unsigned long millis_ticks = CLOCKS_PER_SEC / 1000;
while (clock() - start_ticks < ms * millis_ticks){}
}
int main()
{
int fd = open("/dev/mem", O_RDWR);
gpioAddress = mmap(0, GPIO4_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO4_ADDR_START);
close(fd);
gpio_setdataout_addr = gpioAddress + GPIO_DATA_OFFSET;
gpio_direction_addr = gpioAddress + GPIO_DIR_OFFSET;
*gpio_direction_addr |= GPIO4_PORT;
while (1)
{
*gpio_setdataout_addr |= GPIO4_PORT;
delay(1000);
*gpio_setdataout_addr &= ~(GPIO4_PORT);
delay(1000);
}
}
Compile the project using the GCC toolchain, which you had to manually add to the phytec-qt6demo-image. See the Hello World guide for more nformation.
Target (Linux)
gcc -O blink.c -o blink
You should now see an executable of the name “blink” in the current working directory.
Mux the Processor Pin
In order for this blink executable to work as intended, we also need to ensure that the processor pad is multiplexed such that the GPIO4_IO05 signal is enabled and configured as an output on the UART3_TXD processor pad. We can do this easily using the ‘devmem2’ utility to directly access and modify the i.MX7 hardware registers.
Warning
Use caution when working with the ‘devmem2’ utility as it gives you direct access to read and write to memory that could render the system unstable if you modify memory that is being actively used. This utility has no error checking and shouldn’t be relied upon in production software. PHYTEC recommends working with ‘devmem2’ only while prototyping and transitioning your pin settings to the linux device tree for production.
First, identify the processor pad that brings out the signal we are targeting. This can be done by following X_UART3_TX in the carrier board schematic back to the SOM and to the i.MX7 processor itself. You should find that this signal is accessible at the processor ball number U3, named UART3_TXD.
Looking up the UART3_TXD processor pad within the i.MX7 TRM, we can see that this pad is configured within the IOMUXC_SW_MUX_CTL_PAD_UART3_TX_DATA register which has the memory address 0x3033013C.
We can read this register like so:
Target (Linux)
devmem2 0x3033013C
We should see the following output by default:
Example Output
root@phyboard-zeta-imx7d-1:~# devmem2 0x3033013C /dev/mem opened. Memory mapped at address 0xffff9fc5f000. Read at address 0x3033013C (0xffff9fc5f084): 0x00000005
The value held in the IOMUXC_SW_MUX_CTL_PAD_UART3_TX_DATA memory address is 0x00000005, which can be broken down according to the IOMUXC_SW_MUX_CTL_PAD_UART3_TX_DATA Register Field Descriptions within the TRM (see Section 8.2.7.75). Here we can see that the register is already configured in the GPIO mode we need, so in this case there are no pad config changes necessary.
Run the Blink Program
Once you have the blink executable built and the processor pin is correctly multiplexed to bring out the signal GPIO4_IO05 as an output, we can connect up an LED to X_UART3_TX and blink it!
Reference the following circuit diagram to help you connect up an LED to pin 8 of the X11 “Pi Header” which is located on the PEB-D-RPI Expansion Board on the phyCORE-i.MX7 Development Kit carrier board:
Now run the blink executable and watch the LED blink on and off!
Target (Linux)
./blink
To end the program, use Ctrl + C.