Search by Tags

Using K20 Companion MCU on Apalis TK1

 
Applicable for

Compare with Revision


Subscribe for this article updates

Overview

Using Support Included in BSP

ADC

The ADC driver makes use of the Industrial Input/Output (IIO) framework.

MXM3 Pin K20 Input IIO Device IIO Channel
AD0 PTB0\ADC0_SE8 iio:device0 in_voltage0_raw
AD1 PTB1\ADC0_SE9 iio:device0 in_voltage1_raw
AD2 PTB2\ADC0_SE12 iio:device0 in_voltage2_raw
AD3 PTB3\ADC0_SE13 iio:device0 in_voltage3_raw

The IIO framework exposes those inputs through the sysfs interface:

# cd /sys/bus/iio/devices/iio:device0/
# ls in*
in_voltage0_raw    in_voltage0_scale  in_voltage1_raw    in_voltage1_scale  in_voltage2_raw    in_voltage2_scale  in_voltage3_raw    in_voltage3_scale
# cat /sys/bus/iio/devices/device0/in_voltage0_raw
755

More information about the IIO framework:

  • https://wiki.analog.com/software/linux/docs/iio/iio

Touch Screen

Touch screen controller is implemented in K20 software using internal ADC and external switching. It supports 4-wire resistive touch screens. Input device driver is implemented in kernel.

GPIO

K20 GPIOs are available via sysfs. Since it's not the only GPIO controller available on Apalis TK1 GPIO numbers start with 856. More information on using sysfs GPIO subsystem can be found here.

To calculate the GPIO number use the following formula:

856 + 32 x (Character - 'A') + Digit

So GPIO A0 becomes 856, A7 becomes 863, B0 becomes 888, ...

Not all of the K20 GPIOs can be used, here's the list of available ones with corresponding sysfs id and edge connector pin

GPIO sysfs # MXM3 Pin
PTA3 859 197
PTA5 861 195
PTA17 873 187
PTB16 904 265
PTB17 905 177
PTB18 906 295
PTB19 907 279
PTC0 920 263
PTC1 921 293
PTC2 922 277
PTC3 923 261
PTC4 924 249
PTC6 926 275
PTC7 927 259
PTD0 952 291
PTD1 953 289
PTD2 954 273
PTD3 955 257
PTD4 956 247
PTD5 957 245
PTD6 958 255
PTD7 959 243
PTD8 960 253
PTD9 961 251
PTD11 963 271
PTD12 964 269
PTD13 965 287
PTD14 966 283
PTD15 967 299
PTE0 984 175
PTE1 985 173
PTE2 986 301
PTE3 987 179
PTE4 988 185
PTE5 989 181
PTE24 1008 183
PTE25 1009 191

Erase Chip Without Reprogramming

In order to erase K20 and leave it empty replace firmware file (/lib/firmware/apalis-tk1-k20.bin) with a 1-byte length file.

Easiest way of doing it is to use dd:

dd if=/dev/zero of=/lib/firmware/apalis-tk1-k20.bin bs=1 count=1

after firmware is replaced all you need to do is reload the kernel module or reboot the board.

You should see:

apalis-tk1-k20 spi1.1: Chip fully erased.

in kernel log.

Sources

Kernel modules sources can be found in out kernel git:

http://git.toradex.com/cgit/linux-toradex.git/log/?h=toradex_tk1_l4t_r21.5

K20 firmware is located in freertos git:

http://git.toradex.com/cgit/freertos-toradex.git/log/?h=apalis-tk1-k20-freertos-v8

Custom Firmware

It is possible to use custom software instead of the one provided by us.

Build system setup

Source code provided on our Git is based on KSDK 2.0.0 provided by NXP. Best way of building it is to use Kinetis Design Studio.

In order to work on firmware start by downloading and installing Kinetis Design Studio.

When you have running KDS, select File->Import. In the import window choose General->"Existing Projects into Workspace", on the next screen select project location and make sure that project is detected and selected before continuing. You can deselect "copy project into workspace" if you wish to use project current location. Press Finish. Project should be ready to build and work on.

Communication

K20 and TK1 share single SPI bus and 4 GPIO lines as well as SPI CS and EzPort CS.

TK1 Has control over K20 reset line.

Loading Firmware

Using TK1 SPI with K20 EzPort

Here is an example of how to program binary file into K20 via EzPort.

/*
   Based on spidevlib.c - A user-space program to comunicate using spidev.
                Gustavo Zamboni
*/
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

char buf[64];
char buf2[1024];
int com_serial;
int failcount;

struct spi_ioc_transfer xfer[2];


//////////
// Init SPIdev
//////////
int spi_init(char filename[40])
    {
        int file;
    __u8    mode, lsb, bits;
    __u32 speed=3180000;

        if ((file = open(filename,O_RDWR)) < 0)
        {
            printf("Failed to open the bus.\n");
            /* ERROR HANDLING; you can check errno to see what went wrong */
        com_serial=0;
            exit(1);
            }

        ///////////////
        // Verifications
        ///////////////
        //possible modes: mode |= SPI_LOOP; mode |= SPI_CPHA; mode |= SPI_CPOL; mode |= SPI_LSB_FIRST; mode |= SPI_CS_HIGH; mode |= SPI_3WIRE; mode |= SPI_NO_CS; mode |= SPI_READY;
        //multiple possibilities using |
            mode = SPI_MODE_0;
            if (ioctl(file, SPI_IOC_WR_MODE, &mode)<0)   {
                perror("can't set spi mode");
                return -ENOENT;
                }


            if (ioctl(file, SPI_IOC_RD_MODE, &mode) < 0)
                {
                perror("SPI rd_mode");
                return -ENOENT;
                }
            if (ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0)
                {
                perror("SPI rd_lsb_fist");
                return -ENOENT;
                }
        //sunxi supports only 8 bits
        /*
            if (ioctl(file, SPI_IOC_WR_BITS_PER_WORD, 8)<0)  
                {
                perror("can't set bits per word");
                return;
                }
        */
            if (ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0) 
                {
                perror("SPI bits_per_word");
                return -ENOENT;
                }

            if (ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed)<0)  
                {
                perror("can't set max speed hz");
                return -ENOENT;
                }

            if (ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0) 
                {
                perror("SPI max_speed_hz");
                return -ENOENT;
                }

    printf("%s: spi mode %d, %d bits %s per word, %d Hz max\n",filename, mode, bits, lsb ? "(lsb first) " : "", speed);

    //xfer[0].tx_buf = (unsigned long)buf;
    xfer[0].len = 3; /* Length of  command to write*/
    xfer[0].cs_change = 0; /* Keep CS activated */
    xfer[0].delay_usecs = 0, //delay in us
    xfer[0].speed_hz = speed, //speed
    xfer[0].bits_per_word = 8, // bites per word 8

    //xfer[1].rx_buf = (unsigned long) buf2;
    xfer[1].len = 4; /* Length of Data to read */
    xfer[1].cs_change = 0; /* Keep CS activated */
    xfer[1].delay_usecs = 0;
    xfer[1].speed_hz = speed;
    xfer[1].bits_per_word = 8;

    return file;
    }



//////////
// Read n bytes from the 2 bytes add1 add2 address
//////////

char * spi_read(int addr, int nbytes, int file)
    {
    int status;

    memset(buf, 0, sizeof buf);
    memset(buf2, 0, sizeof buf2);
    buf[0] = addr;
    xfer[0].tx_buf = (unsigned long)buf;
    xfer[0].len = 1; /* Length of  command to write*/
    xfer[1].rx_buf = (unsigned long) buf2;
    xfer[1].len = nbytes; /* Length of Data to read */
    status = ioctl(file, SPI_IOC_MESSAGE(2), xfer);
    if (status < 0)
        {
        perror("SPI_IOC_MESSAGE");
        return NULL;
        }
    //printf("env: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
    //printf("ret: %02x %02x %02x %02x\n", buf2[0], buf2[1], buf2[2], buf2[3]);

    com_serial=1;
    failcount=0;
    return buf2;
    }

//////////
// Read n bytes from the 2 bytes add1 add2 address
//////////

char * spi_read_flash(int addr, int nbytes, int file)
    {
    int status;

    memset(buf, 0, sizeof buf);
    memset(buf2, 0, sizeof buf2);
    buf[0] = 0x0b;
    buf[1] = (addr & 0xFF0000) >> 16;
    buf[2] = (addr & 0xFF00) >> 8;
    buf[3] = (addr & 0xF8);
    buf[4] = 0x00;
    xfer[0].tx_buf = (unsigned long)buf;
    xfer[0].len = 5; /* Length of  command to write*/
    xfer[1].rx_buf = (unsigned long) buf2;
    xfer[1].len = nbytes; /* Length of Data to read */
    status = ioctl(file, SPI_IOC_MESSAGE(2), xfer);
    if (status < 0)
        {
        perror("SPI_IOC_MESSAGE");
        return NULL;
        }
    //printf("env: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
    //printf("ret: %02x %02x %02x %02x\n", buf2[0], buf2[1], buf2[2], buf2[3]);

    com_serial=1;
    failcount=0;
    return buf2;
    }

//////////
// Write n bytes int the 2 bytes address add1 add2
//////////
void spi_write(int addr,int nbytes, uint8_t * out_buf, int file)
    {
    unsigned char buf[32], buf2[32];
    int status;

    if (nbytes > 31)
        return;
    memcpy(&buf[1], out_buf, nbytes);
    buf[0] = addr;
    xfer[0].tx_buf = (unsigned long)buf;
    xfer[0].len = nbytes+1; /* Length of  command to write*/
    status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
    if (status < 0)
        {
        perror("SPI_IOC_MESSAGE");
        return;
        }

    com_serial=1;
    failcount=0;
    }

void spi_write_flash(int addr, int sector ,int nbytes,char value[64],int file)
    {
    unsigned char   buffer[68];
    int status;

    memset(buffer, 0, sizeof buffer);
    buf[0] = addr;
    buf[1] = (sector & 0xFF0000) >> 16;
    buf[2] = (sector & 0xFF00) >> 8;
    buf[3] = (sector & 0xF8);
    memcpy(&buf[4], value, 64);
    xfer[0].tx_buf = (unsigned long)buf;
    xfer[0].len = nbytes+4; /* Length of  command to write*/
    status = ioctl(file, SPI_IOC_MESSAGE(1), xfer);
    if (status < 0)
        {
        perror("SPI_IOC_MESSAGE");
        return;
        }

    com_serial=1;
    failcount=0;
    }

int export_gpios(){
    int sysexport, sysdirection;
    sysexport = open("/sys/class/gpio/export", O_WRONLY);
    if (sysexport < 0)
        return -2;
    //exports 
    write(sysexport, "222", 3); // MCU Reset
    write(sysexport, "178", 3); // EzPort CS
    write(sysexport, "190", 3); // MCU SPI CS (must be high when MCU is being reset for SPI to work)
    close(sysexport);

    //direction & initial value
    sysdirection = open("/sys/class/gpio/gpio222/direction", O_WRONLY);
    if (sysdirection < 0)
        return -2;
    write(sysdirection, "high", 4);
    close(sysdirection);

    sysdirection = open("/sys/class/gpio/gpio178/direction", O_WRONLY);
    if (sysdirection < 0)
        return -2;
    write(sysdirection, "high", 4);
    close(sysdirection);

    sysdirection = open("/sys/class/gpio/gpio190/direction", O_WRONLY);
    if (sysdirection < 0)
        return -2;
    write(sysdirection, "high", 4);
    close(sysdirection);
        sysdirection = open("/sys/class/gpio/gpio82/direction", O_WRONLY);

    if (sysdirection < 0)
        return -2;
    write(sysdirection, "in", 2);
    close(sysdirection);

    sysdirection = open("/sys/class/gpio/gpio74/direction", O_WRONLY);
    if (sysdirection < 0)
        return -2;
    write(sysdirection, "in", 2);
    close(sysdirection);

return 0;
}

#define CS_LOW do {buf1[0] = '0';write(cs, buf1, 1);}while(0)
#define CS_HIGH do {buf1[0] = '1';write(cs, buf1, 1);}while(0)

#define CS_APP_LOW do {buf1[0] = '0';write(cs_app, buf1, 1);}while(0)
#define CS_APP_HIGH do {buf1[0] = '1';write(cs_app, buf1, 1);}while(0)

int main(void) {
    char *buffer;
    char buf1[64];
    char buf2[64];
    char rbuf[64];
    int file, cs, binary, rst;
    int cs_app;
    int read_cnt, file_size = 0;
    int i = 0;

    file = spi_init("/dev/spidev1.2"); //dev
    if (export_gpios() < 0) {
        printf("failed to setup GPIOs\n");
        return -2;
    }

    binary = open("test.bin", O_RDONLY);
    cs = open("/sys/class/gpio/gpio178/value", O_WRONLY);
    cs_app = open("/sys/class/gpio/gpio190/value", O_WRONLY);
    rst = open("/sys/class/gpio/gpio222/value", O_WRONLY);

    if ( file < 0) {
        printf("failed to open spi\n");
        return -2;
    }
    if ( cs < 0) {
        printf("failed to open /sys/class/gpio/gpio178/value\n");
        return -2;
    }
    if ( rst < 0) {
        printf("failed to open /sys/class/gpio/gpio222/value\n");
        return -2;
    }
    if ( binary < 0) {
        printf("failed to open test.bin\n");
        return -2;
    }
    printf("Entering EzPort mode....\t");

    buf1[0] = '1';
    write(rst, buf1, 1);
    usleep(100);

    CS_LOW;
    usleep(100);

    buf1[0] = '0';
    write(rst, buf1, 1);
    usleep(100);

    buf1[0] = '1';
    write(rst, buf1, 1);
    usleep(100);

    CS_HIGH;  
    usleep(100);

    CS_LOW;
    buffer = spi_read(0x05, 1, file);
    CS_HIGH;  
    //printf("EzPort Status Register = 0x%X\n", buffer[0]);

    CS_LOW;
    spi_write(0x06, 0, buf1, file); 
    buf1[0] = '1';write(cs, buf1, 1); 
    CS_HIGH;

    CS_LOW;
    buffer = spi_read(0x05, 1, file);
    CS_HIGH; 
    printf("EzPort Status Register = 0x%X\n", buffer[0]);

    if (buffer[0] != 0x02) {
        printf("FAIL\n");
        close(binary);
        close(cs);
        close(file);
        close(rst);
        close(out);
        return 0;
    }

    printf("Done\n");

    CS_LOW;
    buffer = spi_read_flash(0x400, 16, file);
    CS_HIGH;
    printf("FTFL 0x%X%X%X%X%X%X%X%X \r\n",buffer[7],buffer[6],buffer[5],buffer[4],buffer[3],buffer[2],buffer[1],buffer[0]);
    printf("FTFL 0x%X%X%X%X%X%X%X%X \r\n",buffer[15],buffer[14],buffer[13],buffer[12],buffer[11],buffer[10],buffer[9],buffer[8]);

    printf("EzPort Bulk Erase..."); 
    CS_LOW;
    spi_write(0xc7, 0, buf1, file); 
    CS_HIGH;

    buffer[0] = 0x01;
    /* Wait for WIP flag to clear*/
    while (buffer[0] != 0x00) {
        CS_LOW;
        buffer = spi_read(0x05, 1, file);
        buf1[0] = '1';
        write(cs, buf1, 1);
        if (buffer[0] & 0x40) {
            printf("FAIL\n");
            printf("EzPort WEF asserted!\n");
            close(binary);
            close(cs);
            close(file);
            close(rst);
            close(out);
            return 0;
        }
    }

    printf(" Complete\n"); 

    printf("EzPort Flashing ...");
    while( (read_cnt = read(binary, rbuf, 64)) > 0){
        /* Set WEN bit */
        CS_LOW;
        spi_write(0x06,0,buf1,file); 
        CS_HIGH;

        /* Write flash sector */
        CS_LOW;
        spi_write_flash(0x02, 64*i, 64, rbuf, file);
        CS_HIGH;
        memset(rbuf, 0, sizeof(rbuf));
        i++;
        file_size += read_cnt;

        buffer[0] = 0x01;
        /* Wait for WIP flag to clear*/
        while (buffer[0] != 0x00) {
            CS_LOW;
            buffer = spi_read(0x05, 1, file);
            CS_HIGH;
            if (buffer[0] & 0x40) {
                printf("FAIL\n");
                printf("EzPort WEF asserted!\n");
                    close(binary);
                    close(cs);
                    close(file);
                    close(rst);
                    close(out);
                return 0;
            }
        }
    }

    printf("Done\n");
    printf("EzPort Flash complete, %d bytes wrote.\n", file_size);

    printf("EzPort Flash verification...");
    lseek(binary, 0,SEEK_SET);
    for (int j = 0; j < i; j++) {
        CS_LOW;
        buffer = spi_read_flash(64*j, 64, file);
        CS_HIGH;
        read_cnt = read(binary, rbuf, 64);
        if (memcmp(rbuf, buffer, read_cnt) != 0) {
            printf("FAIL\n");
            printf("Flash verification failed @ 0x%X\n", 64*j);
            return -5;
        }
    } 
    printf("Done\n");

    printf("Resetting the MCU....");
    CS_HIGH;
    buf1[0] = '0';
    write(rst, buf1, 1);
    usleep(100);
    buf1[0] = '1';
    write(rst, buf1, 1);
    printf("Done\n");

    close(binary);
    close(cs);
    close(file);
    close(rst);
    return 0;
}

Using TK1 GPIOs for JTAG

It is possible to use GPIO lines on TK1 with OpenOCD bitbang mode to program\erase the MCU. Example OpenOCD config file (TMS pin (MXM3 197) needs to be connected to the GPIO1 outside the module):

#
# Config for debugging K20 MCU on Apalis TK1
#

interface sysfsgpio

# Each of the JTAG lines need a gpio number set: tck tms tdi tdo
# TCK -> GPIO_PX5 (sysfs 189)
# TMS -> GPIO_PFF2(sysfs 250)
# TDI -> GPIO_PX4 (sysfs 188)
# TDO -> GPIO_PX7 (sysfs 191)
sysfsgpio_jtag_nums 189 250 188 191

# At least one of srst or trst needs to be specified
# MCU_RESET -> GPIO_PBB6 (sysfs 222)
sysfsgpio_trst_num 222
# sysfsgpio_srst_num 24

External Debugger

In order to use external debugger for development or flashing hardware modifications are required. Both SWD and JTAG can be used with proper modifications.