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:
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
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.
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 */
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: