Myszka-60
Elementy technologii żółtych kredensów
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: 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: 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. 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. 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: i z przypiętą drukarką. 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 */
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 */
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: gdzie po kliknięciu na „Send File” i wskazaniu właściwego pliczku, drukarka produkuje taki wydruczek. 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: