Drukarki paragonowe

Pozostałe układy mikrokontrolerów, układy peryferyjne i inne, nie mieszczące się w powyższych kategoriach.
Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Drukarki paragonowe

Postautor: gaweł » sobota 02 lut 2019, 23:41

Drukarki w świecie mikrokontrolerów
Myszka-60


Elementy technologii żółtych kredensów
prn01_ilu00.jpg


Zdarza się czasami, że coś trzeba wydrukować spod procka. Najprostszym rozwiązaniem jest zastosowanie drukarki termicznej z papierem „z rolki”. Drukarki te są przyłączane do czegokolwiek poprzez interfejs szeregowy RS232. Przykładem takiego rozwiązania jest polska drukarka MYSZKA-60. To, że jest „polska” automatycznie oznacza, że drukuje również polskie literki. Ta drukarka umożliwia również wydruk kodów kreskowych. Również dysponuje obcinarką do wydruków sterowaną programowo, można na zakończenie wydruku zażądać odcięcia papieru (tak trochę to "krzywo" działa, bo konieczne jest wydrukowanie przynajmniej 3 pustych wierszy przed obcięciem, bo inaczej wydruk będzie niekompletny).
Jej możliwości najlepiej zaprezentować pokazując wydruk jej autotestu (pokazuje kroje czcionek i inne bajery) jak na poniższych fotografiach:
prn01_ilu01.jpg
prn01_ilu02.jpg
prn01_ilu03.jpg
Ciekawostką jest, i pewną zagwozdką zarazem, że na wydruku jest coś na temat DIPSWICH, natomiast fizycznie jakoś go nie znalazłem. Znaczy w tym egzemplarzu nie znalazłem, natomiast pamiętam z czasów opracowywania „żółtych kredensów”, że takie były. No cóż jest, jak jest. Nie oceniam i nie wnikam (może to jakaś późniejsza wersja trochę okrojona, tak jakoś mi się zawieruszyła).
Kilka widoków na sprzęcior:
prn01_ilu04.jpg
prn01_ilu05.jpg
prn01_ilu06.jpg
Trochę mnie dziwi rozwiązanie złącza w samej drukarce. W moim przekonaniu powinno być damskie, wtedy da się użyć typowy kabelek RS232, a tak trzeba takowy dorobić. W sumie nic trudnego, wystarczy 3-żyłowy kawałek kabelka na linie (z punktu widzenia jakiegoś systemu prockowego lub normalnego kompa): TxD – dane nadawane, DTR – linia modemowa do sygnalizacji, że skończył się papier i GND – masa. Łatwo zauważyć, że drukarka nic nie nadaje, nie ma linii RxD (z punktu widzenia jakiego systemu prockowego).
Schemat środowiska badawczego, w istotnym fragmencie, pokazują poniższe rysunki.
prn01_ilu07.png
Jest to jednostka centralna jakiegoś układu zawierająca procka LPC2368 (w sumie jest mało istotne jaki to procek, wybór padł na ten, bo w tym procku jest pełnomodemowy UART → UART1). Można użyć w sumie dowolnego, jak kontroler nie posiada obsługi linii modemowych, to zawsze takie można zasymulować na normalnych pinach. Więc powyższy procek jest napędzany kwarcem 12MHz, z wewnętrzną obsługą PLL'a. Procek jest programowany w układzie przez dedykowany interfejsik (właściwie to on nic nie zawiera, ma drugi układ ST3232). Przyłączony do proca pełnomodemowy interfejs jest pokazany poniżej.
prn01_ilu08.png
Dodatkowe kanały szeregowe (UART0 i UART2), już bez linii modemowych, bo ich nie ma wychodzą, na świat przez układ ST3232.
Tyle w kwestii hardware. Układ badawczy jest następujący:
prn01_ilu09.jpg
i z przypiętą drukarką.
prn01_ilu10.jpg
Teraz soft.
Drukarka ma polskie litery, których kody odbiegają od tych stosowanych w windoze, więc konieczne jest przekodowanie.
Drukarka wisi na UART1, przez UART2 jest połączenie z terminalem (emulatorem terminala). Z niego można zapodać coś do wydruku. Rozwiązanie jest podobne do stosowanego w „żółtych kredensach”. Tam strumień danych pochodził z sieci, tu zapodaję via serial. Transmitowane dane są identyczne (to znaczy zależy od wersji kredensów, bo tego było wiele, wraz z ewolucją sprzętową kredensów, soft również ulegał ewolucji, właściwie to nic nadzwyczajnego, zawsze jak coś się ulepsza, to i okolice mają fajniej). To rozwiązanie dotyczy pierwszych kredensów.
Dane do wydruku zapodawane były tekstowo. Każdy wiersz poprzedzony był sygnaturą, oznaczająca „a o co chodzi?”. Było to polecenie otwarcia wydruku, dane do wydruku oraz informacja o zamknięciu wydruku. Same dane miały kilka możliwości, można było dane zapodać z kodowaniem w UTF-8 lub w ANSI (no i dodatkowo możliwość druku kodu kreskowego). Normalnie kredensy same z siebie używały kodowania polskich znaków w ANSI, ale jak coś przyszło via WEBSERWICE, bo kredens był na takiej smyczy połączony do centrali, to tam dane chodziły w UTF-8.
Sygnatury są następujące:
[o] – otwarcie dokumentu
[O] – otwarcie dokumentu
[d]dane – wydruk danych tekstowych kodowanych w ANSI,
[D]dane – wydruk danych tekstowych kodowanych w UTF-8,
[b]tekst – wydruk w kodzie kreskowym
[B]tekst – wydruk w kodzie kreskowym
[c] – zamkniecie dokumentu
[C] – zamknięcie dokumentu.

Obsługa strumienia wygląda mniej więcej tak:

Kod: Zaznacz cały

#include "prndriver.h"
#include "uart1.h"
#include "serialconst.h"

#define CR                      0x0D
#define LF                      0x0A
#define ESC                     0x1B

#define CharCodePlSmaLetA       0xB9
#define CharCodePlSmaLetC       0xE6
#define CharCodePlSmaLetE       0xEA
#define CharCodePlSmaLetL       0xB3
#define CharCodePlSmaLetN       0xF1
#define CharCodePlSmaLetO       0xF3
#define CharCodePlSmaLetS       0x9C
#define CharCodePlSmaLetZy      0xBF
#define CharCodePlSmaLetZi      0x9F
#define CharCodePlBigLetA       0xA5
#define CharCodePlBigLetC       0xC6
#define CharCodePlBigLetE       0xCA
#define CharCodePlBigLetL       0xA3
#define CharCodePlBigLetN       0xD1
#define CharCodePlBigLetO       0xD3
#define CharCodePlBigLetS       0x8C
#define CharCodePlBigLetZy      0xAF
#define CharCodePlBigLetZi      0x8F

#define PrinterPlSmaLetA        0xA5
#define PrinterPlSmaLetC        0x86
#define PrinterPlSmaLetE        0xA9
#define PrinterPlSmaLetL        0x88
#define PrinterPlSmaLetN        0xE4
#define PrinterPlSmaLetO        0xA2
#define PrinterPlSmaLetS        0x98
#define PrinterPlSmaLetZy       0xBE
#define PrinterPlSmaLetZi       0xAB
#define PrinterPlBigLetA        0xA4
#define PrinterPlBigLetC        0x8F
#define PrinterPlBigLetE        0xA8
#define PrinterPlBigLetL        0x9D
#define PrinterPlBigLetN        0xE3
#define PrinterPlBigLetO        0xE0
#define PrinterPlBigLetS        0x97
#define PrinterPlBigLetZy       0xBD
#define PrinterPlBigLetZi       0x8D

#define CommandBufferSize       63
typedef struct {
         PrinterServiceProcType   PrinterReadyService ;
         PrinterServiceProcType   PrinterNotReadyService ;
         UCHAR                    CommandBuffer [ CommandBufferSize + 1 ] ;
         USHORT                   BufferSize ;
         USHORT                   Index ;
         UCHAR                    DocumentIsOpened ;
         UCHAR                    PrinterIsReady ;
       } PrinterInstanceRecT ;

typedef struct {
                 UCHAR             InpCode ;
                 UCHAR             OutCode ;
               } EncryptionTableElementType ;
typedef EncryptionTableElementType EncryptionTableType [ 18 ] ;

typedef enum { OpenDocumentOption    ,
               DataAnsiOption        ,
               DataUTF8Option        ,
               BarCodeOption         ,
               CloseDocumentOption   ,
               UnknownOption         } CommandOptionType ;

static PrinterInstanceRecT PrinterInstance ;
 
 
static CommandOptionType SelectCommand ( void )
{
  /*-------------------------------------------------------------------------*/
  if ( PrinterInstance . CommandBuffer [ 0 ] != '[' )
    return UnknownOption ;
  if ( PrinterInstance . CommandBuffer [ 2 ] != ']' )
    return UnknownOption ;
  switch ( PrinterInstance . CommandBuffer [ 1 ] )
  {
    case 'o' :
    case 'O' :
      return OpenDocumentOption ;
    case 'd' :
      return DataAnsiOption ;
    case 'D' :
      return DataUTF8Option ;
    case 'b' :
    case 'B' :
      return BarCodeOption ;
    case 'c' :
    case 'C' :
      return CloseDocumentOption ;
  } /* switch */ ;
  return UnknownOption ;
} /* SelectCommand */


static void RewindPrinterBuffer ( void )
{
  /*-------------------------------------------------------------------------*/
  PrinterInstance . BufferSize = 0 ;
  PrinterInstance . CommandBuffer [ 0 ] = 0 ;
} /* RewindPrinterBuffer */


static const EncryptionTableType LatinEncryptionTable = {
       { CharCodePlSmaLetA      , 'a' } ,
       { CharCodePlSmaLetC      , 'c' } ,
       { CharCodePlSmaLetE      , 'e' } ,
       { CharCodePlSmaLetL      , 'l' } ,
       { CharCodePlSmaLetN      , 'n' } ,
       { CharCodePlSmaLetO      , 'o' } ,
       { CharCodePlSmaLetS      , 's' } ,
       { CharCodePlSmaLetZy     , 'z' } ,
       { CharCodePlSmaLetZi     , 'z' } ,
       { CharCodePlBigLetA      , 'A' } ,
       { CharCodePlBigLetC      , 'C' } ,
       { CharCodePlBigLetE      , 'E' } ,
       { CharCodePlBigLetL      , 'L' } ,
       { CharCodePlBigLetN      , 'N' } ,
       { CharCodePlBigLetO      , 'O' } ,
       { CharCodePlBigLetS      , 'S' } ,
       { CharCodePlBigLetZy     , 'Z' } ,
       { CharCodePlBigLetZi     , 'Z' } } ;

static char LatinTranscription ( char Ch )
{
  USHORT Loop ;
  const EncryptionTableElementType * Element ;
  /*-------------------------------------------------------------------------*/
  Element = LatinEncryptionTable ;
  for ( Loop = 0 ; Loop < 18 ; Loop ++ )
  {
    if ( Element -> InpCode == Ch )
      return Element -> OutCode ;
    Element ++ ;
  } /* for */ ;
  return Ch ;
} /* LatinTranscription */


static const EncryptionTableType AnsiEncryptionTable =
          { { CharCodePlSmaLetA      , PrinterPlSmaLetA   } ,
            { CharCodePlSmaLetC      , PrinterPlSmaLetC   } ,
            { CharCodePlSmaLetE      , PrinterPlSmaLetE   } ,
            { CharCodePlSmaLetL      , PrinterPlSmaLetL   } ,
            { CharCodePlSmaLetN      , PrinterPlSmaLetN   } ,
            { CharCodePlSmaLetO      , PrinterPlSmaLetO   } ,
            { CharCodePlSmaLetS      , PrinterPlSmaLetS   } ,
            { CharCodePlSmaLetZy     , PrinterPlSmaLetZy  } ,
            { CharCodePlSmaLetZi     , PrinterPlSmaLetZi  } ,
            { CharCodePlBigLetA      , PrinterPlBigLetA   } ,
            { CharCodePlBigLetC      , PrinterPlBigLetC   } ,
            { CharCodePlBigLetE      , PrinterPlBigLetE   } ,
            { CharCodePlBigLetL      , PrinterPlBigLetL   } ,
            { CharCodePlBigLetN      , PrinterPlBigLetN   } ,
            { CharCodePlBigLetO      , PrinterPlBigLetO   } ,
            { CharCodePlBigLetS      , PrinterPlBigLetS   } ,
            { CharCodePlBigLetZy     , PrinterPlBigLetZy  } ,
            { CharCodePlBigLetZi     , PrinterPlBigLetZi  } } ;

static char PLTranscription ( char Ch )
{
  USHORT Loop ;
  const EncryptionTableElementType * Element ;
  /*-------------------------------------------------------------------------*/
  Element = AnsiEncryptionTable ;
  for ( Loop = 0 ; Loop < 18 ; Loop ++ )
  {
    if ( Element -> InpCode == Ch )
      return Element -> OutCode ;
    Element ++ ;
  } /* for */ ;
  return Ch ;
} /* PLTranscription */


static void CutPaper ( void )
{
  /*-------------------------------------------------------------------------*/
  UART1Send ( ESC ) ;
  UART1Send ( 'i' ) ;
} /* CutPaper */


static void LineFeed ( void )
{
  /*-------------------------------------------------------------------------*/
  UART1Send ( CR ) ;
  UART1Send ( LF ) ;
} /* LineFeed */


static void PrintAnsiEncoding ( void )
{
  UCHAR * PCh ;
  /*-------------------------------------------------------------------------*/
  PCh = PrinterInstance . CommandBuffer + 3 ;
  while ( * PCh )
  {
    UART1Send ( PLTranscription ( * PCh ) ) ;
    PCh ++ ;
  } /* while */ ;
  LineFeed ( ) ;
} /* PrintAnsiEncoding */


static UCHAR GetNextChar ( void )
{
  UCHAR CurrentChar ;
// Pl UTF-8 char code 
//  0xC4  0x84   A z ogonkiem
//  0xC4  0x85   a z ogonkiem
//  0xC4  0x86   C z kreska
//  0xC4  0x87   c z kreska
//  0xC4  0x98   E z ogonkiem
//  0xC4  0x99   e z ogonkiem
//  0xC5  0x81   L z kreska
//  0xC5  0x82   l z kreska
//  0xC5  0x83   N z kreska
//  0xC5  0x84   n z kreska
//  0xC3  0x93   O z kreska
//  0xC3  0xB3   o z kreska
//  0xC5  0x9A   S z kreska
//  0xC5  0x9B   S z kreska
//  0xC5  0xB9   Z z kreska
//  0xC5  0xBA   z z kreska
//  0xC5  0xBB   Z z kropka
//  0xC5  0xBC   z z kropka
  /*-------------------------------------------------------------------------*/
  CurrentChar = PrinterInstance.CommandBuffer[PrinterInstance.Index++] ;
  if ( CurrentChar == 0xC3 )
  {
    CurrentChar = PrinterInstance.CommandBuffer[PrinterInstance.Index++] ;
    if ( CurrentChar == 0x93 )
      return ( PrinterPlBigLetO ) ;
    if ( CurrentChar == 0xB3 )
      return ( PrinterPlSmaLetO ) ;
    return ' ' ;
  } /* if ... */
  else
  {
    if ( CurrentChar == 0xC4 )
    {
      CurrentChar = PrinterInstance.CommandBuffer[PrinterInstance.Index++] ;
      switch ( CurrentChar )
      {
        case 0x84 :
          return PrinterPlBigLetA ;
        case 0x85 :
          return PrinterPlSmaLetA ;
        case 0x86 :
          return PrinterPlBigLetC ;
        case 0x87 :
          return PrinterPlSmaLetC ;
        case 0x98 :
          return PrinterPlBigLetE ;
        case 0x99 :
          return PrinterPlSmaLetE ;
      } /* switch */ ;
      return ' ' ;
    } /* if ... */
    else
    {
      if ( CurrentChar == 0xC5 )
      {
        CurrentChar = PrinterInstance.CommandBuffer[PrinterInstance.Index++] ;
        switch ( CurrentChar )
        {
          case 0x81 :
            return PrinterPlBigLetL ;
          case 0x82 :
            return PrinterPlSmaLetL ;
          case 0x83 :
            return PrinterPlBigLetN ;
          case 0x84 :
            return PrinterPlSmaLetN ;
          case 0x9A :
            return PrinterPlBigLetS ;
          case 0x9B :
            return PrinterPlSmaLetS ;
          case 0xB9 :
            return PrinterPlBigLetZi ;
          case 0xBA :
            return PrinterPlSmaLetZi ;
          case 0xBB :
            return PrinterPlBigLetZy ;
          case 0xBC :
            return PrinterPlSmaLetZy ;
        } /* switch */ ;
        return ' ' ;
      } /* if ... else */ ;
    } /* if ... else */ ;
  } /* if ... else */ ;
  return CurrentChar ;
} /* GetNextChar */


static void PrintUTF8Encoding ( void )
{
  UCHAR Ch ;
  /*-------------------------------------------------------------------------*/
  PrinterInstance . Index = 3 ;
  for ( ; ; )
  {
    Ch = GetNextChar ( ) ;
    if ( ! Ch )
      break ;
    UART1Send ( Ch ) ;
  } /* for */ ;
  LineFeed ( ) ;
} /* PrintUTF8Encoding */


static void PrintBarCode ( void )
{
  UCHAR * PCh ;
  UCHAR StringSize ;
  /*-------------------------------------------------------------------------*/
  UART1Send ( ESC ) ;
  UART1Send ( 'P' ) ;
  PCh = PrinterInstance . CommandBuffer + 3 ;
  StringSize = 0 ;
  while ( * PCh )
  {
    StringSize ++ ;
    PCh ++ ;
  } /* while */ ;
  UART1Send ( StringSize ) ;
  PCh = PrinterInstance . CommandBuffer + 3 ;
  while ( * PCh )
  {
    UART1Send ( LatinTranscription ( * PCh ) ) ;
    PCh ++ ;
  } /* while */ ;
} /* PrintBarCode */


static void PrintLine ( void )
{
  /*-------------------------------------------------------------------------*/
  if ( PrinterInstance . BufferSize >= 3 )
  {
    if ( PrinterInstance . PrinterIsReady )
    {
      if ( ! CheckPrinterOperable ( ) )
      {
        if ( PrinterInstance . PrinterNotReadyService )
          PrinterInstance . PrinterNotReadyService ( ) ;
        PrinterInstance . PrinterIsReady = FALSE ;
      } /* if */ ;
    } /* if ... */
    else
    {
      if ( CheckPrinterOperable ( ) )
      {
        if ( PrinterInstance . PrinterReadyService )
          PrinterInstance . PrinterReadyService ( ) ;
        PrinterInstance . PrinterIsReady = TRUE ;
      } /* if */ ;
    } /* if ... else */ ;
    if ( PrinterInstance . PrinterIsReady )
    {
      switch ( SelectCommand ( ) )
      {
        case OpenDocumentOption :
          PrinterInstance . DocumentIsOpened = TRUE ;
          break ;
        case DataAnsiOption :
          if ( PrinterInstance . DocumentIsOpened )
            PrintAnsiEncoding ( ) ;
          break ;
        case DataUTF8Option :
          if ( PrinterInstance . DocumentIsOpened )
            PrintUTF8Encoding ( ) ;
          break ;
        case BarCodeOption :
          if ( PrinterInstance . DocumentIsOpened )
            PrintBarCode ( ) ;
          break ;
        case CloseDocumentOption :
          if ( PrinterInstance . DocumentIsOpened )
          {
            LineFeed ( ) ;
            LineFeed ( ) ;
            LineFeed ( ) ;
            CutPaper ( ) ;
          } /* if */ ;
          PrinterInstance . DocumentIsOpened = FALSE ;
          break ;
        case UnknownOption :
          break ;
      } /* switch */ ;
      RewindPrinterBuffer ( ) ;
    } /* if */ ;
  } /* if */ ;
} /* PrintLine */


void ProcessChar ( UCHAR Ch )
{
  /*-------------------------------------------------------------------------*/
  if ( Ch < ' ' )
  {
    if ( Ch == CR )
      PrintLine ( ) ;
  } /* if ... */
  else
  {
    if ( PrinterInstance . BufferSize < CommandBufferSize )
    {
        PrinterInstance . CommandBuffer [ PrinterInstance . BufferSize ] = Ch ;
        PrinterInstance . BufferSize ++ ;
        PrinterInstance . CommandBuffer [ PrinterInstance . BufferSize ] = 0 ;
    } /* if */ ;
  } /* if ... else */ ;
} /* ProcessChar */


UCHAR CheckPrinterOperable ( void )
{
  UCHAR ModemStatus ;
  /*-------------------------------------------------------------------------*/
  ModemStatus = GetInputModemLine ( ) ;
  if ( ModemStatus & DSRControlUARTCode )
    return TRUE ;
  return FALSE ;
} /* CheckPrinterOperable */


void SetPrinterNotify ( PrinterServiceProcType NotReadyNotify ,
                        PrinterServiceProcType ReadyNotify )
{
  /*-------------------------------------------------------------------------*/
  PrinterInstance . PrinterReadyService = ReadyNotify ;
  PrinterInstance . PrinterNotReadyService = NotReadyNotify ;
} /* SetPrinterNotify */


void InitPrinterService ( void )
{
  /*-------------------------------------------------------------------------*/
  RewindPrinterBuffer ( ) ;
  PrinterInstance . DocumentIsOpened = FALSE ;
  PrinterInstance . PrinterIsReady = CheckPrinterOperable ( ) ;
  PrinterInstance . PrinterReadyService = ( PrinterServiceProcType ) 0 ;
  PrinterInstance . PrinterNotReadyService = ( PrinterServiceProcType ) 0 ;
} /* InitPrinterService */
Program do obsługi drukarki, może być następujący:

Kod: Zaznacz cały

#include <LPC23xx.H>
#include "system.h"
#include "types.h"
#include "irqvectors.h"
#include "serialconst.h"
#include "uart1.h"
#include "uart2.h"
#include "prndriver.h"

#define IRQCallCntLimit                 500

static ULONG IRQCallCounter ;

static void SoftwareInit ( void )
{
  /*-------------------------------------------------------------------------*/
  IRQCallCounter = 0 ;
  InitPrinterService ( ) ;
} /* SoftwareInit */


static __irq void T1_IRQHandler ( void )
{
  /*-------------------------------------------------------------------------*/
  IRQCallCounter ++ ;
  if ( IRQCallCounter >= IRQCallCntLimit )
  {
    IRQCallCounter = 0 ;
  } /* if */ ;
  T1IR = 1 ;
  VICVectAddr = 0 ;
} /* T1_IRQHandler */


static void HardwareInit ( void )
{
  /*-------------------------------------------------------------------------*/
  PINSEL10 = 0 ;
  T1MR0 = ( PCLK / 1000 ) - 1 ;
  T1MCR = 3 ;
  T1TCR = 1 ;
  VICVectAddr5 = ( unsigned long ) T1_IRQHandler ;
  VICVectCntl5 = 15 ;
  VICIntEnable = ( 1 << TIMER1_Interrupt ) ;
  UART1InitEnvir ( ) ;
  UART2InitEnvir ( ) ;
} /* HardwareInit */


UCHAR Hello1 [ ]         = "\r\nWitaj koles przy kompie\r\n\r\n[o] - otwarcie dokumentu\r\n[d]dane - wydruk danych tekstowych\r\n[b]liczba - wydruk liczby w kodzie kreskowym\r\n[c] - zamkniecie dokumentu\r\n\r\nNapisz cos:\r\n" ;
UCHAR NoPaperMessage [ ] = "\r\nWloz papier do drukarki\r\n" ;
UCHAR PaperOKMessage [ ] = "\r\nPapier juz jest OK\r\n" ;

static void HelloMessage ( void )
{
  /*-------------------------------------------------------------------------*/
  UART2SendString ( Hello1 ) ;
} /* HelloMessage */


void NoPaper ( void )
{
  /*-------------------------------------------------------------------------*/
  UART2SendString ( NoPaperMessage ) ;
} /* NoPaper */


void PaperOK ( void )
{
  /*-------------------------------------------------------------------------*/
  UART2SendString ( PaperOKMessage ) ;
} /* PaperOK */


int main ( void )
{
  /*-------------------------------------------------------------------------*/
  SysInit ( ) ;
  HardwareInit ( )  ;
  SoftwareInit ( ) ;
  UART1HardwInit( FOSC , N81Mode , 9600 ) ;
  UART2HardwInit( FOSC , N81Mode , 9600 ) ;
  HelloMessage ( ) ;
  if ( ! CheckPrinterOperable ( ) )
    NoPaper ( ) ;
  SetPrinterNotify ( NoPaper , PaperOK ) ;
  for ( ; ; )
  {
    if ( UART2DataPresent ( ) )
      ProcessChar ( UART2GetData ( ) ) ;
  } /* loop */ ;
} /* main */
Do obsługi drukarki „podłącza się” dwie funkcje SetPrinterNotify(NoPaper,PaperOK) do sygnalizacji i reakcji na brak papieru w drukarce. Normalnie w sytuacji braku papieru, to strumień zachowywany był na dysku, by nic nie zaginęło. Tutaj to należałoby wstrzymać się z generowaniem wydruku, a przynajmniej poinformować o takowym zdarzeniu.
Nie chciało mi się „ręcznie za każdym razem wklepywać tego samego”, więc utworzyłem sobie mały pliczek, który był wysyłany via serial do procka armowego. Pliczek jest następujący:
[o]
[d]Witaj koles przy kompie
[d]
[d][o] - otwarcie dokumentu
[d][d]dane - wydruk danych tekstowych
[d][b]liczba - wydruk liczby w kodzie kreskowym
[d][c] - zamkniecie dokumentu
[b]123456789
[d]litera A, i a,: Ą i ą
[d]litera C' i c': Ć i ć
[d]litera E, i e,: Ę i ę
[d]litera L/ i l/: Ł i ł
[d]litera N' i n': Ń i ń
[d]litera O' i o': Ó i ó
[d]litera S' i s': Ś i ś
[d]litera Z- i z-: Ż i ż
[d]litera Z' i z': Ź i ź
[d]
[d]
[c]

Do komunikacji używam takiego programiku:
prn01_ilu11.PNG
gdzie po kliknięciu na „Send File” i wskazaniu właściwego pliczku, drukarka produkuje taki wydruczek.
prn01_ilu12.jpg
Drukarka, … jak drukarka, jest jaka jest. W następnych kredensach były lepsze, głównie z powodu adaptacji do innych alfabetów narodowych. Tu były tylko polskie, a z czasem stały się potrzebne inne, o czym wkrótce napiszę. To będą fajne drukarki od CUSTOM, mam dwa modele: TL60 i TG2460.

Kilka załączników:
KODY_STERUJACE_DRUKAREK_MYSZKA_60PU_60E.pdf
minimyszka_lc_3.pdf
myszka.7z
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
PAMPKIN
Posty: 13
Rejestracja: poniedziałek 07 maja 2018, 08:35

Re: Drukarki paragonowe

Postautor: PAMPKIN » niedziela 03 lut 2019, 14:40

To mnie kolega zaintrygował (czekam na więcej), co do kodu znaków, to jest Latin-2 potwierdza to moja analogowa tablica.

IMG_20190203_142227.jpg
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Drukarki paragonowe

Postautor: gaweł » niedziela 03 lut 2019, 20:37

 
PAMPKIN pisze:To mnie kolega zaintrygował (czekam na więcej), co do kodu znaków, to jest Latin-2 potwierdza to moja analogowa tablica.

Zgadza się, jest o tym napisane w minimyszka_lc_3.pdf, nie napisałem o tym :shock: ? Cóż, przepraszam, jakoś wypadło mi z głowy... ;)

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Drukarki paragonowe

Postautor: gaweł » piątek 15 lut 2019, 13:33

PAMPKIN pisze:To mnie kolega zaintrygował (czekam na więcej)

Bardzo proszę, realizacja zamówienia...

TL80

prn02_ilu00.jpg

Ciekawą propozycję drukarek są urządzenia oferowane przez CUSTOM. Są to również drukarki termiczne przyłączane do systemu za pośrednictwem łącza szeregowego.
Kilka fotek drukarskiego mistrza:
prn02_ilu01.jpg
prn02_ilu02.jpg
prn02_ilu03.jpg
Złącza drukarki, z istotnego punktu widzenia, zawierają wejścia zasilania oraz złącze szeregowe RS232 (w wariancie złącza RJ45).
prn02_ilu04.jpg
W tym przypadku, jak ktoś nie ma odpowiedniego kabelka, to można go sobie zdobyć samodzielnie. Nie jest to jakiś karkołomny wyczyn, w dokumentacji do drukarki jest podany „algorytm kabelka”.
prn02_ilu05.png
Odrobinka własnej pracy i... mamy sukces. Kawałek jakiegoś gotowego kabelka od ethernetu (ma już zakutą złączkę RJ45) i na drugim końcu klasyczny DSUB-9 żeński.
prn02_ilu06.jpg
Finał:
prn02_ilu07.jpg
Mając tak przygotowane środowisko, można zacząć zabawę. Do uzyskania jest wszystko, cokolwiek się zechce. Cały system umożliwia własną definicję czcionek i każdych innych znaków. Idea sprowadza się do modyfikacji pliku wsadu czcionek dla drukarki. W tym celu potrzebne są dwa programy, które można ściągnąć ze strony producenta: http://www.custom.biz. Na stronie głównej klikamy na
prn02_ilu08.png
Klikamy na obrazek drukarki (dla niepoznaki należy kliknąć na TG2460, co za ironia...).
prn02_ilu09.png
Prowadzi do
prn02_ilu10.png
Zjeżdżamy na dół strony do strefy DOWNLOAD.
prn02_ilu11.png
I wjeżdżamy na UTILITIES.
prn02_ilu12.png
Prowadzi to do „ogromnych możliwości” zabawy z maszynerią.
prn02_ilu13.png
Ściągamy dwa programy (będą to pliki typu ZIP, które należy rozpakować i zainstalować).
Jeden jest programem do modyfikacji czcionek, drugi jest programem do ładowania uzyskanych skarbów do drukarki.
Czcionki modyfikuje się następująco. Odpalamy FONTMAKE.
Otwieramy plik: File → Open *.psw.
Ja mam przygotowany już plik po modyfikacjach, więc go otwieram (TL60-polska.PSW).
prn02_ilu16.png
Program pyta, którą tabelę będziemy modyfikować. Należy wskazać numer 2.
prn02_ilu17.png
Klikając na znak w zielonym okienku, otwiera się do modyfikacji font (na przykładzie Ł). Posiłkując się „ołówkiem” można włączać/wyłączać pojedyncze pikseli.
prn02_ilu18.png
Można zmienić absolutnie wszystko, stworzyć własną wizję według własnego uznania, właściwie to limitem jest własna wyobraźnia. Plik (TL60-polska.PSW) zawiera wbite polskie literki o identycznych kodach jak poprzednio. Po zmianach plik należy zapisać jako *.PSW.
Teraz należy odpalić drugi program (UPGCEPRN).
prn02_ilu19.png
Klikając na Select *.PSW, otworzyć zmodyfikowany plik. Aktywują się kolejne przyciski do wykorzystania.
prn02_ilu20.png
Akcja rozpoczyna się po kliknięciu na Begin upgrade.
prn02_ilu21.png
Pozostało wskazać port szeregowy i …pojechało.
prn02_ilu22.png
Po zakończeniu mamy nową rzeczywistość drukarkową.

Dodatkowe materiały:
kody sterujące drukarką
DOMC-0021E.pdf
Dane techniczne
PCTL-60.pdf
Plik fontów drukarki
TL60-polska.zip
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Geek
Geek
Posty: 1259
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: Drukarki paragonowe

Postautor: gaweł » niedziela 24 lut 2019, 23:12

TG2460

prn03_ilu00.jpg


Zbliżoną w swoich możliwościach do PCTL60 jest drukarka PTG2460. Realizuje ona identyczne sekwencje sterujące (program stworzony dla PCTL60 będzie działał również w przypadku drukarki PCTG2460). Chociaż istnieje możliwość rozpoznania modelu przyłączonej drukarki: wystarczy się zapytać: kim jesteś, i dostaje się odpowiedź. Sprawdziłem to i nawet działa (szczegóły można znaleźć w załączonej na końcu dokumentacji). Dla tej drukarki również istnieje możliwość grzebania w fontach. Jak się mocno zaprzeć, można wbić jej literki absolutnie dowolne. Ja mam wbite polskie literki w identyczny sposób jak dla TL60.
prn03_ilu01.jpg
prn03_ilu02.jpg
prn03_ilu03.jpg
prn03_ilu04.jpg
Teraz trochę filozofii softu. W żółtych kredensach istniało wiele programów generujących wydruki. By nie generować niepotrzebnych problemów, istniał program (do obsługi drukarki), który był zasilany danymi pochodzącymi z sieci (dokładniej miał port nasłuchowy na UDP). Dlaczego na UDP? Jest jeden dosyć istotny powód. Jak się przyjrzeć filozofii komunikacyjnej do drukarki istotnym jest by wysłana porcja bez zniekształceń została odebrana przez soft drukarki. Wysłany datagram UDP zawsze jest odebrany jako datagram, w przypadku TCP wysłane porcje (jako drobniejsze kawałki) są często łączone w jeden i do miejsca docelowego dochodzi jako sumaryczna porcja. Nie jest to koniec świata, ale trochę komplikuje program obsługi. Potencjalne niebezpieczeństwo „wcinania się” jednego wydruku w drugi zostało rozwiązane w sposób strukturalny (gdyby przykładowo dwa programy naprzemiennie wysyłały swoje dane do wydruku, to sieć „zsumuje je do jednego”, jednak zostało zadbane by drukarka w danej chwili miała jednego pana i władcę).
W stosunku do pierwszej iteracji kredensów, kolejne miały bardziej rozbudowany „interfejs drukarkowy”. Przykładowo została stworzona możliwość predefiniowania określonych tekstów jako części stałych, także jakiś program użytkowy mógł powiedzieć drukarce, by druknęła dane opisujące lokalizację lub adres. Dodano możliwość sterowania drukiem, czyli określić kiedy jest czcionka bold, italic i temu podobne sterowanie światem.
Jak już wspomniałem, wieszamy drukarkę na UDP'ie. To oznacza, że jest potzrebny kawałek hardware z takimi możliwościami. Tu posłużę się sprzętem od „Infinity”, którego schemat pokazuję poniższe rysunki.
Schemat blokowy:
prn03_ilu05.png
Jednostka centralna z procesorem ARM LPC2378.
prn03_ilu06.png
Interfejs do sieci jako RMII
prn03_ilu07.png
Prymitywne peryferale.
prn03_ilu08.png
Interfejs RS232
prn03_ilu09.png
Złącze IAP.
prn03_ilu10.png
Dodatkowe pamięci: SRAM, jako przedłużacz rozumu operacyjnego oraz kawałek EEPROM na dane, które zawsze należy pamiętać (ot choćby kim się jest).
prn03_ilu11.png
Do tego jest hardware:
prn03_ilu12.png
co realnie przedstawia się następująco:
prn03_ilu13.jpg
Po uzbrojeniu wszystkiego w niezbędne elementy mamy drukarkę poprzez RS232 przypiętą do ARM'owego procka, który z kolei poprzez ethernet jest na smyczy do kompa (kabelki, kabelki i kabelki). Trochę mało jest LED'ów toteż jedyną oznaką życia jest włączony LED od LINK od sieci.
prn03_ilu14.jpg
Sama drukarka wie kim jest i nawet można ją o to zapytać (włączyć zasilanie trzymając przycisk FEED). Drukarka będąca w mojej dyspozycji spowiada się następująco:
prn03_ilu15.jpg
Mam mały program, który wysyła datagramy UDP do drukarki. Dla eksperymentu wysyłam następujące dane:

Kod: Zaznacz cały

[o]
[s]&header;
[s]&address;
[s]&extra;
[s]&source;
[s]&extra;
[d]
[d]
[d]
[d]Druk normalny &boldon;bold &boldoff;normalny
[d]&center;Druk centrowany
[d]&rightjust;Druk z prawej strony
[d]&leftjust;Druk z lewej srony
[d]&center;&invcolor;Druk w negatywie
[d]&center;&normcolor;Druk normalmny
[d]
[d]
[d]
[d]
[d]
[d]
[c]
W stosunku do poprzedniego rozwiązania, dodane są sterowania [S] i [s] (ze wstawkami w UTF-8 i ANSI) do wydruku predefiniowanych danych.
Po wysłaniu powyższych danych wyprodukowany jest następujący wydruk.
prn03_ilu16.jpg
Jak jest to zrobione? Żadna tajemnica.
Program zawiera podstawową obsługę sieci (ARP, ICMP i UDP). Na UDP otwiera port do nasłuchu o odpowiednim numerze. Wszystkie istotne dane sieciowe są następujące:

Kod: Zaznacz cały

#define MachineIPAddress        0xC0A80013
////                              ^^^ 192.168.0.19
#define MachineIPSubnet         0xFFFFFF00
//                              ^^^ 255.255.255.0
#define MachineGWIPAddress      0xC0A800FE
//                              ^^^ 192.168.0.254
#define UDPPortNumber           1025

UCHAR MachineMACAddr [ 6 ]     = { 0x06 , 0x12 , 0x14 , 0x20 , 0x26 , 0x2B } ;
W przykładowym programie stałe fragmenty gry są po prostu podstawione (w ogólnym przypadku może to być rozwiązane w dowolny sposób).

Kod: Zaznacz cały

static const UCHAR HeaderText [ ]       = "Domek na kurzej nozce" ;
static const UCHAR AddressText [ ]      = "W dzikiej puszczy na polanie" ;
static const UCHAR ExtraText [ ]        = "********************" ;


int main ( void )
{
( . . . )

  SetConstText ( HeaderSelection , HeaderText ) ;
  SetConstText ( AddressSelection , AddressText ) ;
  SetConstText ( ExtraSelection , ExtraText ) ;
( . . . )
} /* main */
W danych do druku można „wpleść” sterowania drukiem. Sekwencja zaczyna się znakiem '&', po którym jest identyfikator sterowania zakończony znakiem ';'. Podana sekwencja jest translowana na kody odpowiednie sterujące dla drukarki. Program rozpoznaje następujące magiczne słowa (komentarz chyba nie jest potrzebny):

Kod: Zaznacz cały

typedef void ( * ScriptServiceProcT ) ( void ) ;

typedef struct {
                 UCHAR                  Pattern [ ScriptSymbolSize ] ;
                 ScriptServiceProcT     Service ;
               } ScriptDescrrecT ;

#define ScriptTablesize             36

const ScriptDescrrecT ScriptTable [ ScriptTablesize ] =
     {
/*  0 */ { "&center;"                      , CenterPrinterControl } ,
/*  1 */ { "&leftjust;"                    , LefJustPrinterControl } ,
/*  2 */ { "&rightjust;"                   , RightJustPrinterControl } ,
/*  3 */ { "&cut;"                         , CutPrinterControl } ,
/*  4 */ { "&invcolor;"                    , InverseColorPrinterControl } ,
/*  5 */ { "&normcolor;"                   , NormalColorPrinterControl } ,
/*  6 */ { "&cp0;"                         , CodePage0PrinterControl } ,
/*  7 */ { "&cp1;"                         , CodePage1PrinterControl } ,
/*  8 */ { "&cp2;"                         , CodePage2PrinterControl } ,
/*  9 */ { "&cp3;"                         , CodePage3PrinterControl } ,
/* 10 */ { "&cp4;"                         , CodePage4PrinterControl } ,
/* 11 */ { "&cp5;"                         , CodePage5PrinterControl } ,
/* 12 */ { "&cp6;"                         , CodePage6PrinterControl } ,
/* 13 */ { "&cp7;"                         , CodePage7PrinterControl } ,
/* 14 */ { "&cp8;"                         , CodePage8PrinterControl } ,
/* 15 */ { "&cp9;"                         , CodePage9PrinterControl } ,
/* 16 */ { "&cp10;"                        , CodePage10PrinterControl } ,
/* 17 */ { "&cp11;"                        , CodePage11PrinterControl } ,
/* 18 */ { "&cp12;"                        , CodePage12PrinterControl } ,
/* 19 */ { "&cp13;"                        , CodePage13PrinterControl } ,
/* 20 */ { "&cp14;"                        , CodePage14PrinterControl } ,
/* 21 */ { "&cp15;"                        , CodePage15PrinterControl } ,
/* 22 */ { "&cp16;"                        , CodePage16PrinterControl } ,
/* 23 */ { "&cp17;"                        , CodePage17PrinterControl } ,
/* 24 */ { "&cp18;"                        , CodePage18PrinterControl } ,
/* 25 */ { "&cp19;"                        , CodePage19PrinterControl } ,
/* 26 */ { "&cp20;"                        , CodePage20PrinterControl } ,
/* 27 */ { "&reset;"                       , ResetPrinterControl } ,
/* 28 */ { "&underlon;"                    , UnderLineOnPrinterControl } ,
/* 29 */ { "&underloff;"                   , UnderLineOffPrinterControl } ,
/* 30 */ { "&boldon;"                      , BoldOnPrinterControl } ,
/* 31 */ { "&boldoff;"                     , BoldOffPrinterControl } ,
/* 32 */ { "&italicon;"                    , ItalicOnPrinterControl } ,
/* 33 */ { "&italicoff;"                   , ItalicOffPrinterControl } ,
/* 34 */ { "&size1;"                       , NormalSizePrinterControl } ,
/* 35 */ { "&size2;"                       , DoubleSizePrinterControl } ,
     } ;
Interesujący w drukarce jest sposób rozwiązania sygnalizacji braku papieru. W „Myszce” brak papieru jest sygnalizowany linią modemową. W drukarce Custom można o to zapytać. Po wydruku każdego wiersza, program wysyła do drukarki sekwencję zaczepną (10 hex, 04 hex i 14 hex), na które to drukarka odpowiada 6 bajtami zawierającymi wiele ciekawych informacji (w sofcie jest to PrinterInstance.PrinterStatusReply[6]).

Kod: Zaznacz cały

static void SendStatusAsk ( void )
{
  /*-------------------------------------------------------------------------*/
  UART1Send ( 0x10 ) ;
  UART1Send ( 0x04 ) ;
  UART1Send ( 0x14 ) ;
  PrinterInstance . PrinterStatusService = ReadPrinterStatus ;
  PrinterInstance . PrinterStatusReplyIndex = 0 ;
} /* SendStatusAsk */
Pierwsze dwa bajty są stałe (10 hex 0f hex, taki prefix sterowania) i na kolejnych podane są aktualne szczegóły. Wśród nich można znaleźć, że właśnie skończył się papier, że wkrótce skończy się papier (nie ustaliłem ile metrów przed końcem jest on sygnalizowany). Dodatkowo jest masa innych danych. Przykładowa rozkmina jest następująca:

Kod: Zaznacz cały

static void AnalysePrinterStatus ( void )

{
  unsigned short ReplyByte ;
  /*--------------------------------------------------------------------------*/
  if ( PrinterInstance . PrinterStatusReply [ 0 ] != 0x10 )
  {
// "Pierwszy bajt odpowiedzi jest inny niz 0x10"
    return ;
  } /* if */ ;
  if ( PrinterInstance . PrinterStatusReply [ 1 ] != 0x0F )
  {
// "Drugi bajt odpowiedzi jest inny niz 0x0F"
    return ;
  } /* if */ ;
  ReplyByte = PrinterInstance . PrinterStatusReply [ 2 ] ;
  if ( ReplyByte & 0x01 )
  {
// "Paper status: paper not present,"
  } /* if ... */
  else
  {
// "Paper status: paper present,"
  } /* if ... else */ ;
  if ( ReplyByte & 0x04 )
  {
// "Paper status:  near paper end,"
  } /* if */ ;
  if ( ReplyByte & 0x20 )
  {
// "Paper status: ticket present in output,"
  } /* if ... */
  else
  {
// "Paper status: ticket not present in output,"
  } /* if ... else */ ;
  ReplyByte = PrinterInstance . PrinterStatusReply [ 3 ] ;
  if ( ReplyByte & 0x01 )
  {
// "User status: printing head up error,"
  } /* if */ ;
  if ( ReplyByte & 0x02 )
  {
// "User status: cover opened,"
  } /* if ... */
  else
  {
// "User status: cover closed," ) ;
  } /* if ... else */ ;
  ReplyByte = PrinterInstance . PrinterStatusReply [ 4 ] ;
  if ( ReplyByte & 0x01 )
  {
// "User status: head temp. error," ) ;
  } /* if ... */
  else
  {
// "User status: head temp. ok," ) ;
  } /* if ... else */ ;
  if ( ReplyByte & 0x40 )
  {
// "User status: paper jam," ) ;
  } /* if ... */
  else
  {
// "User status: not paper jam," ) ;
  } /* if ... else */ ;
} /* AnalysePrinterStatus */ ;
gdzie można uzyskać przykładowo informacje czy drukarka jest nagrzana. Więcej można znaleźć w dokumentacji.

Manual drukarki:
TG2460 - user manual.pdf

Kody sterujące:
DOMC-0021E.pdf

Prezentowany soft:
tg2460.zip
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse


Wróć do „Inne mikroklocki, również peryferyjne”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość