SECS-II host using a PIC18F57K42

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
One of my current uC projects is to make a portable SECS (Semiconductor Equipment Communication Standard) host for RS-232 serial (there is also a Ethernet standard) interface troubleshooting of processing equipment. The usual division of responsibility has the IS dept. handling automation to the point of physical connect to tools so the EE dept. depends on them to isolate automation faults. This automation host emulator is designed to eliminate EE equipment problems as the source of communication failures.

The prototype device is being developed on my 'cluttered' home work desk. ;)


The 57k42 was selected mainly because the DMA and vectored interrupt capabilities make it easier to offload the communications and status display workload.
https://www.allaboutcircuits.com/news/microchips-pic18f-k42-family-of-mcus/

The programming environment is xc8 2.05 C99 mode, MPLABX-IDE and MCC.
The LCD display is the EA DOG-M using the spi mode of the controller.
https://www.lcd-module.com/eng/pdf/doma/dog-me.pdf

RS-232 interrupt driven 9600 baud transmissions on top trace with overlapping DMA controlled SPI display transfers (400 kHz, a faster SPI clock causes the display controller to lose characters) on the bottom trace.

SPI clocks and data.

The DMA allows the processor to run at near full speed with no SPI related cpu load or interrupt processing during display updates.

DMA code examples:
C:
/*
* Init the EA DOGM163 in 8-bit serial mode
*/
void init_display(void)
{
    spi_link.tx1a = &ring_buf1;
    spi_link.tx1b = &ring_buf2;
    ringBufS_init(spi_link.tx1a);
    ringBufS_init(spi_link.tx1b);

    SLED = true;
    CSB_SetHigh();
    wdtdelay(350000); // > 400ms power up delay
    send_lcd_cmd(0x39);
    send_lcd_cmd(0x1d);
    send_lcd_cmd(0x50);
    send_lcd_cmd(0x6c);
    send_lcd_cmd(0x76); // contrast last 4 bits
    send_lcd_cmd_long(0x38); // follower control
    send_lcd_cmd(0x0f);
    send_lcd_cmd_long(0x01); // clear
    send_lcd_cmd(0x02);
    send_lcd_cmd(0x06);
    wdtdelay(30);
    SPI1CON2 = 0x02;
    SPI1CON1 = 0x40;
    SPI1CON0 = 0x83;
    SPI1INTFbits.SPI1TXUIF = 0;
    DMA1CON1bits.DMODE = 0;
    DMA1CON1bits.DSTP = 0;
    DMA1CON1bits.SMODE = 1;
    DMA1CON1bits.SMR = 0;
    DMA1CON1bits.SSTP = 1;
    DMA1SSA = (uint32_t) & ring_buf1;
    DMA1CON0bits.DGO = 0;
    SPI1INTFbits.SPI1TXUIF = 1;
    SLED = false;
}

/*
* Trigger the SPI DMA transfer to the LCD display
*/
void start_lcd(void)
{
    DMA1CON0bits.DMA1SIRQEN = 1; /* start DMA trigger */
}

/*
* uses DMA channel 1 for transfers
*/
void eaDogM_WriteString(char *strPtr)
{
    wait_lcd_set();
    /* reset buffer for DMA */
    ringBufS_flush(spi_link.tx1a, false);
    CSB_SetLow(); /* SPI select display */
    if (strlen(strPtr) > max_strlen) strPtr[max_strlen] = 0; // buffer overflow check
    DMA1CON0bits.EN = 0; /* disable DMA to change source count */
    DMA1SSZ = strlen(strPtr);
    DMA1CON0bits.EN = 1; /* enable DMA */
    printf("%s", strPtr); // testing copy method using STDIO redirect to buffer
    DEBUG2_SetLow();
    start_lcd();
    ++V.ticks; // transaction ID for messages
}

/*
* STDOUT user handler function
*/
void putch(char c)
{
    ringBufS_put_dma(spi_link.tx1a, c);
}


SECS-I serial link data rx/tx


Typical SECS-II message detail. S1F1 (ping) Host to Equipment.

Protocol detail: http://www.edgeintegration.com/downloads/Guide_to_understanding_SECS.pdf

Unfortunately most of the official standard details are SEMI and/or company copyright with reproduction limits so they can't be published here but there is plenty of third-party documentation from vendors selling compatible products.

message header structures
C:
/*
* File:   gemsecs.h
* Author: root
*
* Created on February 26, 2019, 2:58 PM
*/

#ifndef GEMSECS_H
#define    GEMSECS_H

#ifdef    __cplusplus
extern "C" {
#endif

#define ENQ    0x05
#define EOT    0x04
#define ACK    0x06
#define NAK    0x15

#include "vconfig.h"
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/uart1.h"
#include "timers.h"

    typedef struct block10_type {
        uint32_t systemb;
        uint8_t bidl;
        uint8_t bidh : 7;
        uint8_t ebit : 1;
        uint8_t function;
        uint8_t stream : 7;
        uint8_t wbit : 1;
        uint8_t didl;
        uint8_t didh : 7;
        uint8_t rbit : 1;
    } block10_type;

    typedef union block10 {
        uint8_t b[10];
        struct block10_type block;
    } block10;

    typedef struct header10 {
        uint16_t checksum;
        union block10 block;
        uint8_t length;
    } header10;

    typedef struct header12 {
        uint16_t checksum;
        uint8_t data[2];
        union block10 block;
        uint8_t length;
    } header12;

    typedef struct header13 {
        uint16_t checksum;
        uint8_t data[3];
        union block10 block;
        uint8_t length;
    } header13;

    typedef struct header14 {
        uint16_t checksum;
        uint8_t data[4];
        union block10 block;
        uint8_t length;
    } header14;

    typedef struct header18 {
        uint16_t checksum;
        uint8_t data[8];
        union block10 block;
        uint8_t length;
    } header18;

    typedef struct header24 {
        uint16_t checksum;
        uint8_t data[14];
        union block10 block;
        uint8_t length;
    } header24;

    typedef struct response_type {
        uint8_t *header;
        uint8_t length;
    } response_type;

    uint16_t block_checksum(uint8_t *, uint16_t);
    uint16_t run_checksum(uint8_t, bool);
    LINK_STATES r_protocol(LINK_STATES *);
    LINK_STATES t_protocol(LINK_STATES *);
    bool secs_send(uint8_t *, uint8_t, bool);
    response_type secs_II_message(uint8_t, uint8_t);

#ifdef    __cplusplus
}
#endif

#endif    /* GEMSECS_H */
This is the current main loop state-machine for testing the protocols and messages with debug code that stuffs the receive buffer with data to simulate equipment serial data streams for error and stress testing.

Standard MCC generated header with my code to follow.
C:
/**
  Generated Main Source File

  Company:
    Microchip Technology Inc.

  File Name:
    main.c

  Summary:
    This is the main file generated using PIC10 / PIC12 / PIC16 / PIC18 MCUs

  Description:
    This header file provides implementations for driver APIs for all modules selected in the GUI.
    Generation Information :
    Product Revision  :  PIC10 / PIC12 / PIC16 / PIC18 MCUs - 1.65.2
    Device            :  PIC18F57K42
    Driver Version    :  2.00
*/

/*
    (c) 2018 Microchip Technology Inc. and its subsidiaries.

    Subject to your compliance with these terms, you may use Microchip software and any
    derivatives exclusively with Microchip products. It is your responsibility to comply with third party
    license terms applicable to your use of third party software (including open source software) that
    may accompany Microchip software.

    THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
    EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY
    IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS
    FOR A PARTICULAR PURPOSE.

    IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
    INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
    WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP
    HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO
    THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL
    CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT
    OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS
    SOFTWARE.
*/

#pragma warning disable 520
#pragma warning disable 1498

#ifndef __DEFINED_int24_t
typedef signed long long int24_t;
#define __DEFINED_int24_t
#endif

#include <stdio.h>
#include <string.h>
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/uart1.h"
#include "eadog.h"
#include "gemsecs.h"
#include "timers.h"

extern struct spi_link_type spi_link;

struct V_data V;
struct header10 H10[] = {
    { // S1F1 send 'are you there?' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 0x000c9f75,
    },
    { // all stream and function header receive buffer from equipment
        .length = 10,
    },
    { // S1F0 send 'ABORT'
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 1,
        .block.block.function = 0,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F1 send 'are you there?' from equipment
        .length = 10,
        .block.block.rbit = 1,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 0x00000d89,
    },
};

struct header12 H12[] = {
    { // S1F2 send 'yes, were are here ' from host
        .length = 12,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 2,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[1] = 1,
        .data[0] = 0,
    },
};

struct header13 H13[] = {
    { // S6F12 send 'online'
        .length = 13,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 6,
        .block.block.function = 12,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};

struct header14 H14[] = {
    { // S1F4 send 'status response '
        .length = 14,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 4,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};

struct header18 H18[] = {
    { // S1F3 send 'status request '
        .length = 18,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 3,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};

struct header24 H24[] = {
    { // S2F18 send 'host time '
        .length = 24,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 2,
        .block.block.function = 18,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};

struct header10 r_block;

volatile uint16_t tickCount[TMR_COUNT] = {0};

/*
            Main application
*/
void main(void)
{
    uint16_t sum;
    UI_STATES mode = UI_STATE_HOST; /* link configuration host/equipment/etc ... */

    // Initialize the device
    SYSTEM_Initialize();

    // Enable high priority global interrupts
    INTERRUPT_GlobalInterruptHighEnable();

    // Enable low priority global interrupts.
    INTERRUPT_GlobalInterruptLowEnable();

    V.ui_state = UI_STATE_INIT;

    while (true) {
        switch (V.ui_state) {
        case UI_STATE_INIT:
            init_display();
            V.ui_state = mode;
            V.s_state = SEQ_STATE_INIT;
#ifdef TESTING
            uint8_t j;

            j = 3; // set H10 block for testing
            sum = block_checksum((uint8_t*) & H10[j].block.block, sizeof(block10));
            H10[j].checksum = sum;
            sprintf(V.buf, "M %d, H %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x, C 0x%04x #",
                mode,
                H10[j].block.b[9],
                H10[j].block.b[8],
                H10[j].block.b[7],
                H10[j].block.b[6],
                H10[j].block.b[5],
                H10[j].block.b[4],
                H10[j].block.b[3],
                H10[j].block.b[2],
                H10[j].block.b[1],
                H10[j].block.b[0],
                sum);
            wait_lcd_done();
            eaDogM_WriteString(V.buf);

            secs_send((uint8_t*) & H10[j], sizeof(header10), false);
            sprintf(V.buf, " C 0x%04x #", V.t_checksum);
            wait_lcd_done();
            eaDogM_WriteString(V.buf);
#endif

            break;
        case UI_STATE_HOST:
            switch (V.s_state) {
            case SEQ_STATE_INIT:
                V.r_l_state = LINK_STATE_IDLE;
                V.t_l_state = LINK_STATE_IDLE;
                V.s_state = SEQ_STATE_RX;
#ifdef DB1
                WaitMs(75);
                UART1_put_buffer(ENQ);
#endif
                break;
            case SEQ_STATE_RX:
                /*
                * receive message from equipment
                */
                if (r_protocol(&V.r_l_state) == LINK_STATE_DONE) {
                    sprintf(V.buf, " S%dF%d #", V.stream, V.function);
                    wait_lcd_done();
                    eaDogM_WriteString(V.buf);
#ifdef DB1
                    WaitMs(5);
#endif
                    V.s_state = SEQ_STATE_TX;
                }
                if (V.r_l_state == LINK_STATE_ERROR)
                    V.s_state = SEQ_STATE_ERROR;
                break;
            case SEQ_STATE_TX:
                /*
                * send response message to equipment
                */
                if (t_protocol(&V.t_l_state) == LINK_STATE_DONE) {
                    V.s_state = SEQ_STATE_TRIGGER;
                }
                if (V.t_l_state == LINK_STATE_ERROR)
                    V.s_state = SEQ_STATE_ERROR;
                break;
            case SEQ_STATE_TRIGGER:
                DEBUG1_SetHigh();
                sprintf(V.buf, " OK #");
                eaDogM_WriteString(V.buf);
                if (wait_lcd_check())
                    V.s_state = SEQ_STATE_DONE;
                DEBUG1_SetLow();
                break;
            case SEQ_STATE_DONE:
                V.s_state = SEQ_STATE_INIT;
                break;
            case SEQ_STATE_ERROR:
            default:
                UART1_Write(NAK);
                sprintf(V.buf, " ERR R%d T%d E%d A%d #", V.r_l_state, V.t_l_state, V.error, V.abort);
                wait_lcd_done();
                eaDogM_WriteString(V.buf);
                V.s_state = SEQ_STATE_INIT;
                break;
            }
            break;
        case UI_STATE_ERROR:
        default:
            V.ui_state = UI_STATE_INIT;
            break;
        }
    }
}
/**
End of File
*/
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
The comm link 'block' protocol:

Single page 'fair use'

Folded XC8 C source for the serial line block protocol. ;)


The SECS-II message protocol that uses the block SECS-I protocol for actual machines is quite messy but here we are only testing, so canned responses are used with simple logic.

C:
// code fragments from the protocol source files

    typedef struct response_type {
        uint8_t *header;
        uint8_t length;
    } response_type;

/*
* parse stream and response codes into a message pointer and length to send in response
*/
response_type secs_II_message(uint8_t stream, uint8_t function)
{
    static response_type block;

    V.abort = LINK_ERROR_NONE;

    switch (stream) {
    case 1:
        switch (function) {
        case 1: // S1F2
            block.header = (uint8_t*) & H12[0];
            block.length = sizeof(header12);
            H12[0].block.block.systemb = V.systemb;
            break;
        case 2: // S1F1
            block.header = (uint8_t*) & H10[0];
            block.length = sizeof(header10);
            H10[0].block.block.systemb = V.systemb;
            break;
        case 3: // S1F4
            block.header = (uint8_t*) & H14[0];
            block.length = sizeof(header14);
            H14[0].block.block.systemb = V.systemb;
            break;
        case 4: // S1F3
            block.header = (uint8_t*) & H18[0];
            block.length = sizeof(header18);
            H18[0].block.block.systemb = V.systemb;
            break;
        default: // S1F0 abort
            block.header = (uint8_t*) & H10[2];
            block.length = sizeof(header10);
            H10[2].block.block.systemb = V.systemb;
            V.abort = LINK_ERROR_ABORT;
            break;
        }
        break;
    case 2:
        switch (function) {
        case 17: // S2F18
            block.header = (uint8_t*) & H24[0];
            block.length = sizeof(header24);
            H24[0].block.block.systemb = V.systemb;
            break;
        default: // S1F0 abort
            block.header = (uint8_t*) & H10[2];
            block.length = sizeof(header10);
            H10[2].block.block.systemb = V.systemb;
            V.abort = LINK_ERROR_ABORT;
            break;
        }
        break;
    case 6:
        switch (function) {
        case 11: // S6F12
            block.header = (uint8_t*) & H13[0];
            block.length = sizeof(header13);
            H13[0].block.block.systemb = V.systemb;
            break;
        case 12: // S6F11
            block.header = (uint8_t*) & H53[0];
            block.length = sizeof(header53);
            H53[0].block.block.systemb = V.systemb;
            break;
        default: // S1F0 abort
            block.header = (uint8_t*) & H10[2];
            block.length = sizeof(header10);
            H10[2].block.block.systemb = V.systemb;
            V.abort = LINK_ERROR_ABORT;
            break;
        }
        break;
    default: // S1F0 abort
        block.header = (uint8_t*) & H10[2];
        block.length = sizeof(header10);
        H10[2].block.block.systemb = V.systemb;
        V.abort = LINK_ERROR_ABORT;
        break;
    }

    return(block);
}
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086

Prototype Controller with internal controller protocol error codes. S5F1 Alarm Report Send from equipment.

The small Omron relay near the display is used to connect the uart2 receiver instead of uart1 transmitter on the incoming communications rx/tx data lines (uart1 receiver remains connected) when in 'TX/RX monitor both mode' while the equipment is connected to the normal automation host controller with a tee monitor plug.

Host online to equipment sequence. Host Controller 'Watch at 0.25 speed'

Same Host online to equipment sequence. Equipment display

Transaction flowchart
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
All of the designed for equipment types connect to the uC like the normal host controller so the basic hardware and software design is at the beta stage. Ordered three prototype PCB's (RPi board form-factor compatible) from OSH Park for field testing an a week or so when all the parts arrive. Hopefully the first board design works without too many 'jumpers'.:D
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
One of the functions built into this tester is a built-in test unit. I need to communicate as a host and diagnose various communications line errors the equipment and host might see. Because generating those types of errors in a predictable and reproducible manner is impossible with real systems a loop-back function has been designed into the software for robustness testing while being developed. The XC8 srand/rand number generator (that repeats the same 'random' sequence from the start depending on the initial seed) is used to inset several types of errors on the internally generated loopback data stream. Random checksum errors. random protocol control errors and random message failures provide a way to test the ability of the system to detect, recover and log errors for long duration link tests in the testers host and monitor modes.

The first step is to add a UART routine that will stuff the receive buffer with locally generated data.
The uart1.c MCC generated file was modified with this added routine.

C:
/* stuff the uart1 receive buffer with testing data */
void UART1_put_buffer(uint8_t bufData)
{
    PIE3bits.U1RXIE = 0;
    uart1RxBuffer[uart1RxHead++] = bufData;
    if (sizeof(uart1RxBuffer) <= uart1RxHead) {
        uart1RxHead = 0;
    }

    uart1RxCount++;
    PIE3bits.U1RXIE = 1;
}


The standard secs message transmission routine has a 'fake' option that uses the stuffing routine to emulate equipment messages from the RS-232 line.


#ifdef DB2
        WaitMs(50);
        if (V.uart == 1)
#ifdef RERROR
            if (rand() < ERROR_COMM) // sometimes forget to send the message
#endif
                secs_send((uint8_t*) & H27[0], sizeof(header27), true, V.uart);
        if (V.uart == 2)
#ifdef RERROR
            if (rand() < ERROR_COMM)
#endif
                secs_send((uint8_t*) & H10[0], sizeof(header10), true, V.uart);
#endif

/* send the whole sequence including length and checksum bytes */
bool secs_send(uint8_t *byte_block, uint8_t length, bool fake, uint8_t s_uart)
{
    uint8_t i, *k;
    uint16_t checksum;

    k = (uint8_t *) byte_block;

    ++V.ticks; // transaction ID for host master messages
    V.error = LINK_ERROR_NONE;
    if ((length - 3) != k[length - 1]) { // check header length field byte
        V.error = LINK_ERROR_SEND;
        return false; // don't send and return mismatch error
    }

    /*
    * space up from bottom two bytes and don't include last byte
    * for checksums
    * we send C structures from the max byte to first in uC memory
    */
    checksum = block_checksum(&k[2], length - 3);
    k[0] = checksum & 0xff;
    k[1] = (checksum >> 8)&0xff;
    V.t_checksum = checksum;

    switch (s_uart) {
    case 2:
        while (UART2_is_tx_ready() < 64); // wait for tx buffer to drain
        for (i = length; i > 0; i--) {
            if (fake) {
                UART2_put_buffer(k[i - 1]);
            } else {

                UART2_Write(k[i - 1]); // -1 for array memory addressing
            }
        }
        break;
    case 1:
    default:
        while (UART1_is_tx_ready() < 64); // wait for tx buffer to drain
        for (i = length; i > 0; i--) {
            if (fake) {
                UART1_put_buffer(k[i - 1]);
            } else {

                UART1_Write(k[i - 1]); // -1 for array memory addressing
            }
        }
        break;
    }

    return true;
}


The message checksum routine and protocol control byte transmitters have random number generated errors.


/*
* Checksum for message and header block after length byte
*/
uint16_t block_checksum(uint8_t *byte_block, uint16_t byte_count)
{
    uint16_t sum = 0, i;

    for (i = 0; i < byte_count; i++) {
        sum += byte_block[i];
    }
#ifdef RERROR
    if (rand() > ERROR_CHECKSUM)
        sum++; // make a bad checksum
#endif
    return sum;
}

#ifdef DB4
WaitMs(5);
#ifdef RERROR
if (rand() < ERROR_COMM) // sometimes forget to send the ACK
#endif
UART1_put_buffer(ACK);
#endif
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Back to work. My three V1.0 PCBs arrived from OSH Park. I need to add a couple of zero ohm resistors and a move a couple of components for better fit but everything seems to work fine and it fits the plastic RPi case firmly.

Started the parts install on the board with the uC and 16MHz chip OSC. I'm also testing the new DSOX1204G on this build.

16MHz CPU clock signal on I/O pin. METCAL RF solder station and hot air gun for soldering components.


All of the important parts installed for signal and operational testing. The top connector is for SPI display or aux device comms. The X connectors are CPU pin breakouts.


Optional SPI display signal testing. The normal text based I/O channel will be on the equipment/host text terminal service via the SECS-II message protocol once the host is in online-remote
with the serial port connected equipment interface.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Found a 'bug' on the program. :D



Display prototype via 1 meter 400 kHz SPI/Power interface cable. 82 ohm series resistors in all signal lines to reduce ringing.




The trusty old Tek2465A with delayed sweep can easily see the ringing on the falling edges at the LCD and controller but at 400 kHz it doesn't matter.

Using delayed sweep on a Tek246* scope.
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Built a new SECS/GEM host controller with the next (and last) rev. boards.


New rev. on the right.

While populating the board I noticed something strange about the GND/VSS VDD/VCC jumper zero ohm resistors. The resistor sheet for zero ohms actually had 32 ohm resistors instead of the correct values.



I rechecked them all and more that one value in the new box was mislabeled in the low ohm range selection. :mad: Somebody has QA issues.
Murphy never sleeps.:D
 
Last edited:

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
A little primer for why this odd-ball serial binary protocol is used today even with fast networks.

https://www.cimetrix.com/blog/secs-gem-series-protocol-layer

Arrays of numbers are brutally efficient in SECS/GEM. The overhead for an array is 1 byte for the type plus 1 to 4 bytes for the length of the array, plus the data in its native size. For example: an array of 10 4-byte integers would take 42 bytes, that is a data density of 95%!

In the JSON example, a 4-byte integer requires 16 bytes + the number of characters needed to represent the integer, so 17 to 28 bytes. Floating point numbers are the same overhead, but probably requiring more characters to represent the value.

In XML, the overhead is based on the sizes of the XML element names. Using the element names in the example above, for any 4 -byte integer the number of bytes across the wire will be 9 + number of characters needed to represent the integer, so 10 to 21 bytes. Floating point numbers are at the mercy of the string formatting used to represent the values.

In summary, looking at the per-item byte size across the wire, SECS/GEM is very dense. Take the 4-byte integer example where SECS/GEM is 6 bytes across the wire, the JSON example is 17 to 28 and the XML example is 10 to 21 bytes and you see as you scale the number of parameters the overhead really matters. 300mm Semiconductor equipment are expected to transfer 1000 parameters per second per process module to the host. For a 2-module equipment, this results in the following number of bytes just for the data: 12K bytes/ over SECS/GEM, 34K-56K for JSON, and 20K-42K for the XML example. These numbers do not account for size of the rest of the message, just the actual parts related to parameter values. If that data is transferred in lots of messages with few values per message, then the network load is even worse. Fewer, larger messages are always better in all cases.
The display PCB has been designed and ordered for the initial set of in-house field test units as software development continues (it never ends).

 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Display PCB.



Checking the SPI signal waveform at the display module.


How cheap is too cheap. When the enclosure screw shears off while being screwed into the plastic mounting hole.:(
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
One last board spin for USB power. Users wanted to plug the unit directly into a battery or laptop. The only other hardware change was to use nice Omron lighted switches for the run and mode button indicators with PWM for brightness control.

B3W-9002-G1C
B3W-9000-R1R


https://www.mouser.com/datasheet/2/307/en-b3w-9-11824.pdf


It runs just fine from a power bank but because of the low current drain the bank auto-shutoff activates on the test battery. I already have smd pads for a power drain resistor but I'd rather not waste the power.
Anyone have suggestions for a reliable power bank with an option to disable auto-shutoff mode?
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Original prototype running a vacuum load-lock diagnostic sequence. Started from the equipment SECS terminal by sending the 's' command to the PIC host controller.
 

Thread Starter

nsaspook

Joined Aug 27, 2009
13,086
Another K42 board spin for a replacement universal touch controller. Much the same as above.
IMG_20190610_152245743.jpg
The original design used a 18f8722 board, this design uses the18f47k42 controller on a custom board. It takes a standard LCD ELO touch-screen monitor like this touchscreen and converts that to several older standard CRT overlay touch standards usable for screen modernization upgrades. The video above shows a LCD upgrade screen using the older touch converter.

Original touch converter controller.



Original LCD and controller for the CRT replacement program from years ago. Unfortunately the original replacement LCD is now obsolete
so this new controller design will be for the latest edition of the touch monitor to replace the old LCD units.
 
Top