Komunikační interface EZS Olympo Controls UNI1 s MCU SAB80C537

      25.11.2019 Do ruky se mi dostala už poměrně zastaralá deska univerzálního komunikačního interface Olympo Controls UNI1 (bez obvodu GALEXP), který slouží pro připojení ústředen elektronického zabezpečovacího systému řady Galaxy a Ultimate k bezdrátovým pultům centrální ochrany Fautor, Radom, NAM, ADT a Securitas nebo přes telefonní komunikátory Matilda KOM4.1 či APHIS. Lze k němu také připojit paralelní tiskárnu pro výpisy událostí (na desce je i NV paměť pro uložení posledních 64 událostí). Ústředna EZS se připojuje buď přes RS-232 na konektor P1 (ústředna tam posílá data pro sériovou tiskárnu) nebo přes RS-485 na svorkovnici X1. Rozhraní se vybírá pomocí dvojice jumperů JP1 (nasunuté vlevo - RS-232, vpravo RS-485) a přes transceivery je připojeno na UART0 řídicího MCU. Informace o událostech jsou překódovány a posílány dle nastavení na druhý RS-232 výstup pro PCO na konektor P2 (UART1 na MCU), jenž slouží také pro připojení k PC při konfiguraci. Paralelní tiskárnu lze připojit ke konektoru P4 a telefonní komunikátor ke konektoru P3. Napájení 12 V (pozor, přepěťová ochrana zabírá už někde při 14 - 15 V) se připojuje ke svorkovnici X4. Na svorkovnici X2 je vstup pro tamper spínač a na svorkovnici X3 jsou přes relátka výstupy tamperu a poruchy komunikace. Typ ústředny, PCO, komunikátorů a konfigurační režim se volí 8-pólovým DIP přepínačem.

UNI1 layout

      Samotná deska komunikačního interface bez ústředny a komunikátorů mi je celkem na 2 věci, ale chtěl jsem si aspoň zkusit s ním popovídat a vyčíst paměť událostí. Podařilo se mi stáhnout a nainstalovat program Unikonf98 v2.50. Připojil jsem desku k PC přes sériový port P2, DIP přepínač jsem nastavil do režimu programování (DIP7 = OFF, DIP8 = ON), zapnul napájení a spustil Unikonf98, ale ten s interfejsem zatvrzele odmítal komunikovat (chyba přenosu, UNI-1 nepřipojen). Deska se chovala úplně stejně, jako když byl DIP přepínač nastavený v normálním provozním režimu. U konektoru tiskárny mi neustále bliká červená LED (i když jsem tiskárnu připojil) a na sériový port to pravidelně po 10 s posílá stále stejný paket "#0A000000000000000000002D" (při tom vždy blikne zelená LED u konektoru P2). Tamper vstup mám propojený. Zkusil jsem také vytisknout konfiguraci na připojenou paralelní tiskárnu oběma způsoby (nastavení DIP4, 5, 6 = ON a stisknout RESET tlačítko - tisk po předchozí inicializaci a nastavení DIP4, 5, 6, 8 = ON, RESET, DIP8 = OFF, RESET - opakovaný tisk), ale vůbec nic, ani žádná změna chování LED, vzdávám to...

      17.1.2020 Když jsem přemýšlel co s deskou dál, říkal jsem si, že by byla škoda ji rozebrat. Mohla by docela dobře posloužit jako takový vývojový kit pro platformu x51. Na desce je totiž docela našlapaný MCU (na poměry jednapadesátek) SAB80C537 od Infineonu taktovaný z 11,0592 MHz krystalového oscilátoru s připojenou 64kB externí pamětí programu EPROM M27C512 (v patici), externí 32kB datovou SRAM GM76C256CLL a externí paralelní EEPROM AT28C64B, k tomu 2 UARTy s převodníky a nějaké ty GPIO. Plošňák je pouze 2-vrstvý, takže nedalo zas tak moc práce překreslit alespoň základní schéma zapojení (časem ho možná ještě doplním). MCU a paměti jsou zapojené víceméně podle katalogu: port P0 je vyhrazený pro multiplexovaný dolní Byte dat a adresy, který se ukládá do latche 74HCT573N řízeného signálem ALE (Address Latch Enable), port P2 je vyhrazený pro horní Byte adresy, paměť programu je selektována pomocí signálu PSEN# (Program Store ENable), z datové paměti se čte pomocí signálu RD# na portu P3.7 a zapisuje se do ní signálem WR# na portu P3.6, výběr mezi datovou pamětí RAM a EEPROM se provádí pomocí portu P1.5 a logiky z NAND hradel SN74LS00N. Externí datová paměť začíná na adrese 0 a přistupuje se do ní přes 16-bitové pointery instrukcí MOVX. Interní datová paměť 256 B začíná také na adrese 0, ale přistupuje se k ní instrukcí MOV. Dolních 128 B lze adresovat přímo (nejrychlejší přístup), horních 128 B pouze nepřímo (o něco pomalejší). Speciální funkční registry (SFR) jsou v rozsahu adres 80 - FFh a adresují se přímo instrukcí MOV. Interní programová paměť (pokud MCU nějakou má), začíná na adrese 0 a externí pak navazuje. SAB80C537 žádnou interní ROM nemá, takže externí začíná hned na adrese 0 a přistupuje se k ní přes 16-bitové pointery instrukcí MOVC.
      Aby se na desce dalo alespoň trochu rozumně vyvíjet, nahradil jsem EPROMku s okýnkem za pinově kompatabilní 64kB EEPROM Winbond W27E512, kterou krmím LabProgem 48LV. Přeci jen mazání EPROMek horským sluncem jsem si kdysi užil dost. Ještě lepší by byl simulátor EPROM přímo zapojený do desky a k PC, holt dnes už jsme nehorázně zhýčkaní ISP a JTAGem. Po programování v assembleru na Z80 a AT89C2051 se mi taky zrovna nestýská, naštěstí máme k dispozici zdarma OpenSource Céčkový kompilátor SDCC, který podporuje různé 8-bitové MCU a už jsem ho použil dříve na STM8. Nejprve jsem si naprogramoval blikání žlutou LEDkou. Porty P0 - P5 podporují atomické nastavení jednotlivých bitů a v SDCC tak můžeme přímo přiřazovat hodnotu, např. P1_3 = 1 pro bit 3 na portu P1. Program fungoval na první pokus.
      Pak jsem přidal inicializaci UARTu 0 (vyvedený na konektor P1). Bitová rychlost je odvozena buď z dedikovaného baudrate generátoru (zapíná se bitem BD v registru ADCON) dle vztahu baudrate = 2^SMOD*fXtal/2496 (bit SMOD v registru PCON zapíná zdvojnásobení bitové rychlosti) a je optimalizovaný pro frekvenci krystalu 12 MHz, avšak na desce je osazen krystal s oblíbenou frekvencí 11,0592 MHz vhodnou pro vydělení na standardní rychlosti UARTu s pomocí časovače T1, který běží v 8-bitovém autoreload režimu a pak baudrate = (2^SMOD*fXtal) / (32*12*(256-TH1)). Konfigurační a stavový registr UARTu 0 S0CON obsahuje 2 interrupt flagy TI0 pro indikaci odvysílání posledního bitu a RI0 pro indikaci příjmu posledního bitu. Oba směřují na společný vektor přerušení SI0_VECTOR, ale přerušení zatím v programu nepoužívám. Tyto flagy je pak třeba programově shodit zápisem 0.
      Nakonec jsem do hlavní smyčky přidal kód pro vyčtení externí datové paměti EEPROM pomocí pointeru do adresního prostoru __xdata (další adresní prostory jsou značeny: __data - interní přímo adresovatelná RAM, __idata - interní nepřímo adresovatelná RAM, __code - programová paměť, __sfr - interní registry SFR). K mému zklamání však byla celá paměť EEPROM vymazaná (na hodnotu 0). Abych se ujistil, že čtu z EEPROM správně, přidal jsem ještě kód, který na začátek EEPROM zapíše řetězec "Pokus", který jsem také správně přečetl (i po zakomentování a vypnutí/zapnutí napájení). Ukázkový program následuje níže, lze ho přeložit příkazem: sdcc -mmcs51 --out-fmt-ihx --std-c99 --opt-code-size --model-small --iram-size 256 --xram-size 32768 --code-size 65536 pokus.c


#define MICROCONTROLLER_SAB80517       // set MCU type for 80C537 (80C537 is ROM-less)
#define MCS51REG_EXTERNAL_ROM          // reserve P0, P2 for external ROM
#define MCS51REG_EXTERNAL_RAM          // reserve P0, P2, P3.6(WR#), P3.7 (RD#) for XRAM
#include <stdint.h>
#include <mcs51reg.h>

#define LED_YP     P1_3                // yellow LED at bit-addresable output port P1.3
#define LED_G3P    P1_4                // green LED 3 at bit-addresable output port P1.4
#define RAM_EE_SEL P1_5                // ext. RAM/EEPROM# select at bit-addresable output port P1.5

__xdata uint8_t *__data p_xdata=0;     // pointer physically in internal RAM pointing to XRAM
uint16_t i=0;

void delay_ms(uint16_t t)
{
  volatile uint16_t i;
  for (i=0; i<t*50; i++);              // 50000 loops ~ 1000ms with 11,0592MHz Xtal
}

void uart_putc(char c)
{
  S0BUF=c;                             // transmit the character via UART0
  while (TI0==0);                      // wait until the last bit is transmitted
  TI0=0;                               // clear the transmit interrupt flag
}

void uart_print(const char *str)
{
  while (*str!=0)                      // until string termination character is reached
    {
    S0BUF=*str;                        // transmit a character via UART0
    while (TI0==0);                    // wait until the last bit is transmitted
    TI0=0;                             // clear the transmit interrupt flag
    str++;                             // increment string pointer to next character
    }
}

void main(void)
{                                      // baudrate = (2^SMOD*fXtal) / (32*12*(256-TH1))
  TMOD=0x21;                           // set timer 1 to 8-bit auto-reload mode
  TL1=0xFD;                            // set timer 1 value
  TH1=0xFD;                            // set timer 1 reload value
  TR1=1;                               // run timer 1 for baudrate generation
  S0CON=0x50;                          // set UART0 to 8-bit mode (1) with var. baudrate 9600 baud
  PCON&=0x7F;                          // set SMOD=0 - disable baudrate doubling
  BD=0;                                // set ADCON.BD=0 - disable dedicated baudrate gen.

  uart_print("Hello UNI1 SAB80C537, terminal: 9600,8,n\n");

/*
  RAM_EE_SEL=0;                        // select external EEPROM
  p_xdata[0]='P';                      // write data to external EEPROM
  p_xdata[1]='o';                      // write data to external EEPROM
  p_xdata[2]='k';                      // write data to external EEPROM
  p_xdata[3]='u';                      // write data to external EEPROM
  p_xdata[4]='s';                      // write data to external EEPROM
  RAM_EE_SEL=1;                        // select external RAM
*/

  while (1)
    {
    LED_YP=0;
    delay_ms(50);
    LED_YP=1;
    delay_ms(50);

    if (i<0x2000)                      // external 8kB EEPROM dump
      {
      RAM_EE_SEL=0;                    // select external EEPROM
      uart_putc(p_xdata[i++]);         // read and send data from ext. EEPROM
      RAM_EE_SEL=1;                    // select external RAM
      }
    }
}


      Někdy v budoucnu bych pak mohl zkusit napsat nějaký bootloader, který by umožňoval nahrát nový program po UARTu. Tento MCU ale nemá HW podporu pro bootloader a přepínání interrupt vektorů jako třeba Atmel AVR. Mohlo by to jít tak, že bych kód bootloaderu umístil někam na konec programové paměti a první instrukcí bych na něj skočil. Bootloader by si ze začátku přijaté binárky přečetl startovací adresu, někam ji uložil (třeba do EEPROM) a přepsal by ji svojí startovací adresou tak, aby se vždy zajistilo jeho spuštění po resetu. Po ukončení běhu bootloaderu by se provedl skok na dříve uloženou adresu začátku hlavního programu.

      7.2.2020 Jak jsem zjistil, v SDCC je možno vyřešit výpisy po sériové lince i jednodušeji, stačí si napsat vlastní funkci int putchar(int) a pak už standardně používat funkce printf() a puts() ze stdio.h. Formátovací řetězec je automaticky uložen v paměti programu (ROM), netřeba ošetřovat žádnou pakárnou typu PSTR() jako v avr-gcc. Je k dispozici více variant funkce printf(), které se liší velikostí kódu (0,26 - 3,7 kB) a podporovanými výstupními formáty, více viz dokumentace, str. 54. Dále jsem si napsal funkci pro obsluhu přerušení UARTu (ISR), abych mohl přijímat příchozí příkazy a implementoval jednoduchý shell. ISR lze psát jednoduše v C, stačí funkci deklarovat pomocí klíčového slova __interrupt(n) s číslem vektoru za názvem funkce, např. void uart0_isr(void) __interrupt(SI0_VECTOR). Nepovinným atributem __using(n) lze také definovat, kterou banku registrů překladač použije pro ukládání lokálních proměnných. Pokud chceme napsat ISR v inline assembleru, můžeme ještě připojit atribut __naked, který vynechá automatické generování kódu pro ukládání a obnovu registrů na začátku a konci funkce (musíme se pak o to v kódu postarat sami).
      Když jsem podrobněji zkoumal datasheet paměti W27E512, zjistil jsem, že možnost ISP (programování přímo v desce) nebude možná bez HW úprav. Tato paměť je totiž hodně stará a vyžaduje pro mazání a programování zvýšené napětí na pinech OE#/VPP a A9 (12 - 14 V). To by vyžadovalo přerušit vodivé cesty, dát tam diody (aby se VN nedostalo do ostatních čipů) a nějakými P-FETy připínat vyšší napájecí napětí přes nevyužité GPIO. Navíc na desce jsou spojené piny paměti CE# (20) a OE#/VPP (22). Zde jsou shrnuty kombinace úrovní na pinech pro různé režimy:
mazání: OE#/VPP = 14 V, A9 = 14 V, A0 - A8, A10 - A15 = L, D0 - D7 = H, 100 ms L puls na CE#
programování: OE#/VPP = 12 V, A0 - A15 = adresa, D0 - D7 = data, 100 µs L puls na CE#
čtení VID (DAh): OE#/VPP = L, CE# = L, A0 = L, A9 = 12 V
čtení PID (08h): OE#/VPP = L, CE# = L, A0 = H, A9 = 12 V.
Mám i kupu modernějších 5V-only FlashROM pamětí, ale ty jsou buď ve větším pouzdru DIL32 nebo SMD PLCC32 místo DIL28, takže bude potřeba udělat nějakou redukci. Když se dívám na pinout např. paměti AT29C010A, tak má v levé řadě nahoře 2 nadbytečné piny 1 - nezapojený, 2 - A16 (stačí uzemnit, přes odpor připojit na VCC nebo spojit s A15) a v pravé řadě nahoře jsou 2 piny 32 - VCC a 31 - WE#, ostatní piny jsou stejné jako u kratší paměti, akorát pin 30 je nezapojený. Takže VCC spojit se stávajícím VCC, WE# na nějaký GPIO, OE# nechat na PSEN# a CE# připojit na zem. Jenže je zde jeden zásadní problém: při pokusu o zápis do paměti bude MCU neustále generovat další čtecí cykly při čtení každé instrukce, čímž softwarově prováděný zápisový cyklus úplně rozbije. Je tedy nutné, aby kód provádějící zápis do jedné (v tu chvíli neaktivní) paměti běžel z jiné paměti. Bohužel zde použitý MCU nemá žádnou vnitřní paměť, kterou by šlo pro bootloader využít, jako mají modernější 51, kde se to dá vyřešit např. takto.



Zpět

Aktualizováno 10.2.2020 v 3:33