RT1020-EVK with ENET and SPI

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

RT1020-EVK with ENET and SPI

Jump to solution
1,722 Views
jon1
Contributor III

I have been asked to undertake a project which requires SPI data to be captured and sent over ethernet using an RT1020-EVK for development/evaluation purposes. I am still awaiting delivery of my RT1020-EVK but looking at the schematics for the EVK, it appears that the ethernet interface is sharing the same GPIO pins as the SPI (available on the J19 header). I presume both interfaces cannot be used at the same time so what I am trying to achieve may not be possible. Is this assumption correct ?

I have never programming this type of device before so please excuse if what I am asking seems obvious to other people.

Labels (1)
1 Solution
1,616 Views
mjbcswitzerland
Specialist V

Jon

The i.MX Rt 1021 has 4 LPSPIs so you can, for example, use LPSPI3 on J18:
- pin 3 GPIO_AD_B1_12 -> LPSPI3_SCK
- pin 4 GPIO_AD_B1_13 -> LPSPI3_PCS0
- pin 5 GPIO_AD_B1_14 -> LPSPI3_SDO
- pin 6 GPIO_AD_B1_15 -> LPSPI3_SDI

If you need to demonstrate SPI to Ethernet you can use the uTasker project which has this function ready as off-the-bat complete solution (either sending as UDP or TCP packets at programmable intervals), including complete simulation of the operation for analysing, debugging and further developing in Visual Studio.

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]

i.MX RT 1020: https://www.utasker.com/iMX/RT1020.html

GPIO video: https://www.youtube.com/watch?v=SmFTi8hlba0&list=PLWKlVb_MqDQFZAulrUywU30v869JBYi9Q&index=29

View solution in original post

5 Replies
1,616 Views
jon1
Contributor III

I forgot to mention that the SPI is acting as a data logger which is required to be sent over ethernet every 1ms (not that this is important) so I doubt dynamically enabling/disabling each interface when access is required is a solution.

0 Kudos
1,617 Views
mjbcswitzerland
Specialist V

Jon

The i.MX Rt 1021 has 4 LPSPIs so you can, for example, use LPSPI3 on J18:
- pin 3 GPIO_AD_B1_12 -> LPSPI3_SCK
- pin 4 GPIO_AD_B1_13 -> LPSPI3_PCS0
- pin 5 GPIO_AD_B1_14 -> LPSPI3_SDO
- pin 6 GPIO_AD_B1_15 -> LPSPI3_SDI

If you need to demonstrate SPI to Ethernet you can use the uTasker project which has this function ready as off-the-bat complete solution (either sending as UDP or TCP packets at programmable intervals), including complete simulation of the operation for analysing, debugging and further developing in Visual Studio.

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]

i.MX RT 1020: https://www.utasker.com/iMX/RT1020.html

GPIO video: https://www.youtube.com/watch?v=SmFTi8hlba0&list=PLWKlVb_MqDQFZAulrUywU30v869JBYi9Q&index=29

1,616 Views
jon1
Contributor III

Hi Mark,

Thanks for your detailed reply.

I was only looking at the schematics for the board and it didn't mention that J18 also contained the LPSPI3 connections so thanks very much for pointing this out to me.

It does suggest that the same 4 GPIO pins are also used by other peripherals :

pastedImage_1.png

I assume if I am driving LPSPI3 on the same pins, it won't affect/conflict with the operation of the other peripherals? I see that USB_OTG1_OC is connected to the fault input of the USB power supply circuit so if we are driving the same pin with the LPSPI3 clock signal output, this won't interfere with anything ?

Sorry for sounding a numpty but this is my first foray into this area of programming so it is all new to me.

Thanks,

Jon

0 Kudos
1,616 Views
mjbcswitzerland
Specialist V

Jon

Since the EVBs are very general there are often some conflicts - in the case of USB_OTG1_OC there is a chip pulling this down in USB fault conditions, which looks to be present all the time if not using the USB host mode. If you remove the resistor R65 is is then OK. (you may well find other LPSPI combinations that are also possible on the board).

I have attached binary files for the i.MX RT 1020 EVK which reads a number of bytes of data from an SPI slave and sends the samples as payload in UDP frames every 1ms and you can also build this yourself with the uTasker project. Below are guides, or links to guides where appropriate: if you are using the board and/or MCUXpresso for the first time there are a few details to be learned but with the step-by-steps you should be finished in a half an hour or so and have an operational reference (wither using the pre-buillt binaries or building it yourself).

1. Loading the binaries:
This may look a bit complicated the first time but the uTasker project uses by default a boot loader concept and complete encryption and cloning protection as standard and needs the loader programmed (this is not an afterthought in the project but fundamental - it doesn't otherwise make the project any more difficult to use). Usually this needs to be done only once and then just applications can be loaded.
- uTaskerBootLoaderImage.bin is the primary boot loader image which can be loaded by connecting the debugger USB cable (that powers the board). You will see the board as an external hard drive "RT1020-EVK". You need to set the board's DIP jumpers to OFF-OFF-OFF-ON (otherwise the loading fails) and press the reset button. Drag and drop this file on the disk (it may take a few seconds because the loader seems to delete an area in the flash, which takes some time, before actually programming the code). [Don't worry about anything being locked on the board because the reference binaries don't actually secure the chip from JTAG access or further ISP programming]
- you need a second USB cable connect to the processor's USB too and after you set the DIP switches back to OFF-OFF-ON-OFF and reset the board it will appear as "FALLBACK_LD" disk. Now drag and drop the file "uTaskerSerialLoaderUpload.bin" on to it. Now you essentially have a completely secured system (although JTAG is still open and this would be set to secure mode before really delivering secure products) that allows both serial loader and application upgrading via USB or OTA (over-the-air).
- The board now appears as "UPLOAD_DISK" and you can drag and drop the application "uTaskerV1.4_AES256_application.bin" and you are done - the application will now run your reference application.

2. Using the reference:
- the default IP of the board is 192.168.0.3 and it will try to send the samples (100 bytes per ms) to a server at 192.168.0.100 (on UDP port 23456)
- the IP setup can be changed and saved in the LAN menu that appears on the LPUART's VCOM connection at 115kBaud - the menu should be self-explanatory and you can view the setting with "show_config". If you want to change the MAC address use the secret command "MAC 00-00-00-00-00-65" for example, where it allows just a single change from its default 00-00-00-00-00-00. The default MAC address will also work though.
- After 4s the board will start sending the sampled data (you can also monitor the SPI signals - it sends out 0x55 for every byte read in and uses 13MHz clock).
- Since will will probably not have a server on the destination IP address you will see that ARP requests are being sent initially, trying to resolve the destination. ARP repetitions are fairly slow so there is not a lot of traffic (watch with Wireshark, for example).
- If you can't prepare a destination to respond it is not a problem because you can do the following:
In the LAN menu command "prime 192.168.0.100" which will prime an entry of this address in the board's ARP table and it will then send the UDP frames, which can see being transmitted aver 1ms. The content will be 0xff if there is no slave connected (floating input at '1').

In case of an issue with no Ethernet transmission please do the following:
- go to the Administrator menu (4) and command "reset"
I don't have this problem with other EVKs but sometimes the 1020EVK looks to have an issue with its PHY after a power reset and this solves it. I will be investigating more later...

3. To get details of the boot loader concept see this video :
https://www.youtube.com/watch?v=2XfgZq19XDw&list=PLWKlVb_MqDQEOCnsNOJO8gd3jDCwiyKKe&index=3

4. For general developers guides see https://www.utasker.com/iMX/developers.html

If you are new to the i.MX RT 1021 especilly check out: https://www.utasker.com/docs/iMX/i.MX_RT_1021_uTasker.pdf which explains how simple it is to control clock setups (making it logical rather than a black-box) and such.

5. To build with MCUXpresso you can use this video guide: https://youtu.be/C7DyE8RscHs

6. To add your project's application to the uTasker project you can use this configuration (in config.h)
- select your board with #define MIMXRT1020

- ensure #define ETH_INTERFACE is enabled so that you have Ethernet
- ensure #define USE_UDP is enabled (you can also active other services like FTP or HTTP servers)
- enable #define QUICK_DEV_TASKS so that additional tasks are prepared for code in the next step

7. To add your application (as in the attached binary) you can use this code in application.c

use the following code, by overwriting the general code with
extern void fnQuickTask1(TTASKTABLE *ptrTaskTable)
extern void fnQuickTask2(TTASKTABLE *ptrTaskTable) and
extern void fnQuickTask3(TTASKTABLE *ptrTaskTable)

#define SAMPLE_UDP_PORT_NUMBER  23456
#define SAMPLE_BUFFER_SIZE      100
typedef struct stUDP_SAMPLE_MESSAGE
{
    unsigned short usLength;
    UDP_HEADER     tUDP_Header;                                      // reserve header space
    unsigned char  ucUDP_Message[SAMPLE_BUFFER_SIZE];                // reserve message space
} UDP_SAMPLE_MESSAGE;

static unsigned char ucUDP_IP_Address[IPV4_LENGTH] = { 192, 168, 1, 100 }; // address to send UDP frames to
static USOCKET SamplesUDP_Socket = -1;
static UDP_SAMPLE_MESSAGE *ptrSample_UDP_Frame = 0;


extern void fnQuickTask1(TTASKTABLE *ptrTaskTable)                       // TASK_DEV_1
{
    // Configure LPSPI3
    //
    #define SPI_CS1_0                  PORT1_BIT29                       // control the CS line as GPIO output
    _iMX_LPSPI *ptrLPI2C = (_iMX_LPSPI *)LPSPI3_BLOCK;                   // use LPSPI3
    POWER_UP_ATOMIC(1, LPSPI3_CLOCKS);                                   // ungate clocks to the LPSPI module
    ptrLPI2C->LPSPI_CR = (LPSPI_CR_RST | LPSPI_CR_DBGEN);                // command module reset
    _CONFIG_PERIPHERAL(GPIO_AD_B1_12, LPSPI3_SCK, (UART_PULL_UPS | PORT_DSE_MID)); // select LPSPI3_SCK on GPIO1-28 - alt function 2
    _CONFIG_PERIPHERAL(GPIO_AD_B1_14, LPSPI3_SDO, (UART_PULL_UPS | PORT_DSE_MID)); // select LPSPI3_SDO on GPIO1-30 - alt function 2
    _CONFIG_PERIPHERAL(GPIO_AD_B1_15, LPSPI3_SDI, (UART_PULL_UPS | PORT_DSE_MID)); // select LPSPI3_SDI on GPIO1-31 - alt function 2
    _CONFIG_DRIVE_PORT_OUTPUT_VALUE(1, SPI_CS1_0, SPI_CS1_0, (PORT_SRE_FAST | PORT_DSE_MID));
    ptrLPI2C->LPSPI_CR = (LPSPI_CR_DBGEN);                               // release from reset
    _WAIT_REGISTER_TRUE(ptrLPI2C->LPSPI_CR, LPSPI_CR_RST);               // wait until the reset has been negated before continuing
    ptrLPI2C->LPSPI_CCR = 0;                                             // additional timing can be configured here
    ptrLPI2C->LPSPI_CR = (LPSPI_CR_MEN | LPSPI_CR_DBGEN);                // enable
    ptrLPI2C->LPSPI_CFGR1 = LPSPI_CFGR1_MASTER;                          // set master mode of operation
    ptrLPI2C->LPSPI_TCR = (LPSPI_TCR_CPHA | LPSPI_TCR_CPOL | LPSPI_TCR_PRESCALE_4 | LPSPI_TCR_FRAMESZ_8); // set phase, polarity, word size and speed [105MHz/4/2 = 13.1MHz]

    // Configure USB socket for sending sampled data over
    //
    ucUDP_IP_Address[2] = temp_pars->temp_network->ucOurIP[2];           // ensure the local submask matches our configuration
    if ((SamplesUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, 0, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) >= 0) {
        fnBindSocket(SamplesUDP_Socket, SAMPLE_UDP_PORT_NUMBER);         // bind socket to UDP port
        ptrSample_UDP_Frame = uMalloc(sizeof(UDP_SAMPLE_MESSAGE));       // get some memory for UDP frame
        uTaskerMonoTimer(TASK_DEV_2, (DELAY_LIMIT)(4 * SEC), 1);         // start second task after a 4s delay
    }
}

static __callback_interrupt void int_1ms(void)
{
    uTaskerStateChange(TASK_DEV_3, UTASKER_ACTIVATE);                    // schedule the task to perform sampling
}

extern void fnQuickTask2(TTASKTABLE *ptrTaskTable)                       // TASK_DEV_2
{
    // Configure a repetitive 1ms timer using PIT2
    //
    PIT_SETUP pit_setup;                                                 // PIT interrupt configuration parameters
    pit_setup.int_type = PIT_INTERRUPT;
    pit_setup.mode = PIT_PERIODIC;
    pit_setup.ucPIT = 2;                                                 // use PIT2
    pit_setup.int_handler = int_1ms;                                     // interrupt handler
    pit_setup.int_priority = PIT2_INTERRUPT_PRIORITY;
    pit_setup.count_delay = PIT_US_DELAY(1000);                          // 1000us period
    fnConfigureInterrupt((void *)&pit_setup);                            // configure PIT
}

extern void fnQuickTask3(TTASKTABLE *ptrTaskTable)                       // TASK_DEV_3
{
    // Read SPI input data to a buffer and send it over the UDP socket
    //
    ptrSample_UDP_Frame->usLength = 0;
    _CLEARBITS(1, SPI_CS1_0);                                            // assert the CS line
    while (ptrSample_UDP_Frame->usLength < SAMPLE_BUFFER_SIZE) {
        LPSPI3_TDR = (0x55);                                             // send a byte over the LPSPI in order to read in the next value
        _WAIT_REGISTER_TRUE(LPSPI3_RSR, LPSPI_RSR_RXEMPTY);              // wait until teh value has been received
        ptrSample_UDP_Frame->ucUDP_Message[ptrSample_UDP_Frame->usLength++] = (unsigned char)LPSPI3_RDR; // put the sample into the UDP payload
    }
    _SETBITS(1, SPI_CS1_0);                                              // negate the CS line

    fnSendUDP(SamplesUDP_Socket, ucUDP_IP_Address, SAMPLE_UDP_PORT_NUMBER, (unsigned char *)&ptrSample_UDP_Frame->tUDP_Header, ptrSample_UDP_Frame->usLength, 0); // send samples in UDP frame
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

That is in fact all code needed for your application. It should be fairly easy to understand (it could of course be solved in many different ways but this is easy to follow) and the general concept used is :

- fnQuickTask1() will be called once when the system is up (including Ethernet initialised). It configures the LPSPI and creates a UDP socket for sending the data with later and then starts fnQuickTask2() after a 4s delay (this will allow the Ethernet link to come up, for example , but is not really necessary).
- fnQuickTask2() configures a PIT channel to generate a 1ms periodic interrupt (handled by int_1ms()).
- int_1ms() schedules the third task at exact 1ms rate.
- fnQuickTask3() [executed every 1ms] reads in 100 bytes of data on the LPSPI to a UDP payload buffer and sends it out to the remote host address.

You can play around with settings and you shouldn't have any real difficulty to get your real logging data into the stream.

Good luck

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]

Finally note that this can also be completely tested in Visual Studio, where the chip is simulated, allowing simpler learning and code reviews. The project allows huge increases in development efficiency (and cost saving) compared to the more traditional methods that many developers are still using...

If you want to run the exact same project on any other i.MX RT board (or on any Kinetis part) just change the board define in config.h and the work is done.. (you would just need to set up the SPI pins to match).

1,616 Views
jon1
Contributor III

Hi Mark,

Thanks again for your detailed response.

I think I have enough information now to solve my problem.

Jon

0 Kudos