/* ----------------------------------------------------- picomonb_uart.c TFT-LCD 160x128 / 128x128 / 160x80 / 320x240 als Mini-Terminalmonitor ueber UART angesprochen. ESC-Steuersequenzen fuer Farben und fuer Cursor- steuerung werden ebenso interpretiert wie Standard- steuercodes ( \n \r \t \b ) Display scrollt bei Erreichen der untersten Zeile den Text automatisch nach oben MCU : CH32V003 Takt : 22.04.2025 R. Seelig ------------------------------------------------------ */ /* CH32V003F4P6 +--------------+ PD4 / a7 | 1 20 | PD3 / a4 PD5 / a5 / utx | 2 C 19 | PD2 / a3 PD6 / a6 / urx | 3 H 18 | PD1 / swio nrst / PD7 | 4 3 17 | PC7 / miso PA1 / osc0 / a0 | 5 2 16 | PC6 / mosi PA2 / osc1 / a1 | 6 V 15 | PC5 / sck / scl gnd | 7 0 14 | PC4 / a2 PD0 | 8 0 13 | PC3 Vdd | 9 3 12 | PC2 / scl PC0 | 10 11 | PC1 / sda +--------------+ */ #include #include #include #include #include "ch32fun.h" #include "ch32v003_gpio.h" #include "uart.h" #include "my_printf.h" #include "tft_termdisplay_v2.h" #include "turtlegraph.h" #define printf my_printf #define BAUD 4800 #define but_init() PD2_input_init() #define is_but() (!(is_PD2())) uint8_t progrun = 0; // nimmt das naechste zu startende Programm auf // ----------------------------------------------------------- // Ringpuffer fuer serielle Schnittstelle USART // ----------------------------------------------------------- #define RECV_BUF_SIZE 256 // Puffergroesse Ringspeicher uint8_t recv_buf[RECV_BUF_SIZE]; volatile int recv_buf_next; // naechste Adresse an der eingegangenes Zeichen // abgelegt wird volatile int recv_buf_current; // Adresse, an der das naechste Zeichen zu lesen ist // ----------------------------------------------------------- // Turtlegrafik // ----------------------------------------------------------- char turtlestring[256]; // nimmt einen String auf, der als Zeichenstiftgrafik // interpretiert wird // ----------------------------------------------------------- // Speicherbedarf des Textframebuffers festlegen // ----------------------------------------------------------- uint8_t fb[xt_max * yt_max * 2]; int fb_adr = 0; // ----------------------------------------------------------- // globale Variable des Frequenzzaehlers // ----------------------------------------------------------- volatile uint32_t frequency = 0; volatile uint32_t counter32= 0; volatile uint8_t newval= 0; /* -------------------------------------------------------- fb_clear loescht den Textframebuffer -------------------------------------------------------- */ void fb_clear(void) { int i; for (i= 0; i< (xt_max * yt_max * 2); i++) fb[i]= 0; } /* -------------------------------------------------------- fb_show zeigt den Inhalt des Textframebuffers an -------------------------------------------------------- */ void fb_show(void) { int i; uint8_t b, ch; bkcolor= 0; clrscr(); wherex= 0; wherey= 0; gotoxy(wherex, wherey); for (i= 0; i< (xt_max * yt_max * 2); i+= 2) { b= fb[i]; bkcolor= (b >> 4); textcolor= (b & 0x0f); ch= fb[i+1]; if (ch) { lcd_putchar(ch); } wherex++; if (wherex> (xt_max-1)) { wherex= 0; wherey++; } gotoxy(wherex, wherey); } } /* -------------------------------------------------------- fb_scrollup scrollt den Framebuffer um eine Zeile nach oben und zeigt diesen an -------------------------------------------------------- */ void fb_scrollup(void) { int ind1, ind2, i; int x,y; uint8_t txc, bkc; x= wherex; y= wherey; txc= textcolor; bkc= bkcolor; ind1 = 0; ind2 = xt_max*2; for (i= 0; i< (xt_max * (yt_max-1) * 2); i++) { fb[ind1]= fb[ind2]; ind1++; ind2++; } for (i= xt_max * (yt_max-1) * 2; i< (xt_max * yt_max * 2); i++) { fb[i]= 0x00; } fb_show(); wherex= x; wherey= y; gotoxy(wherex, wherey); textcolor= txc; bkcolor= bkc; } /* ---------------------------------------------------------- USART1_IRQ Interruptvektor Interruptvektor, wird aufgerufen, wenn auf der UART ein Zeichen eingegangen ist ! ---------------------------------------------------------- */ void USART1_IRQHandler(void) __attribute__((interrupt)); void USART1_IRQHandler(void) { int cx; // Timeoutzaehler int i; { cx= 0; do { if (USART1->STATR & USART_STATR_RXNE) // wenn ein eingegangenes Zeichen den Interrupt // ausgeloest hat { // wenn ein Zeichen empfangen wurde, dieses im Ringspeicher ablegen recv_buf[recv_buf_next] = USART1->DATAR; // Test auf Ueberlauf i = (recv_buf_next + 1) % RECV_BUF_SIZE; if (i != recv_buf_current) { recv_buf_next = i; } } cx++; } while (((USART1->STATR & USART_STATR_RXNE) != 0) && (cx< 10000)); } } /* -------------------------------------------------------- timer2_init -------------------------------------------------------- */ void timer2_init(void) { RCC->APB1PCENR |= (RCC_APB1Periph_TIM2); TIM2->CTLR1 |= TIM_CKD_DIV1; TIM2->PSC= 12; TIM2->ATRLR= 1; TIM2->SWEVGR |= TIM_UG; TIM2->DMAINTENR |=(TIM_IT_Update); TIM2->CTLR1 |= TIM_CEN; NVIC_SetPriority(TIM2_IRQn,5); NVIC_EnableIRQ(TIM2_IRQn); } void TIM2_IRQHandler() __attribute__( ( interrupt() ) ); void TIM2_IRQHandler() { counter32++; TIM2->INTFR &=~(uint16_t)TIM_IT_Update; } /* -------------------------------------------------------- pd0_pinchange_init initialisiert PD0 fuer Interruptbetrieb bei auf- tretendem Logikwechsel, hier von high nach low (falling edge = negative Flanke) -------------------------------------------------------- */ void pd0_pinchange_init(void) { RCC->APB2PCENR |= RCC_APB2Periph_AFIO; // Takt fuer alternate Funktion GPIO einschalten PD0_float_init(); // PD0 als Input schalten AFIO->EXTICR = AFIO_EXTICR1_EXTI0_PD; // _EXTI4_PD => PD4 // _EXTI3_PC => PD3 ... etc. EXTI->INTENR = EXTI_INTENR_MR0; // enable extern Interrupt Line 4 // _INTENR_MR3 => Line 3, waere also PD3 EXTI->FTENR = EXTI_FTENR_TR0; // Trigger abfallende Flanke auf Line 0, FTE = falling trigger edge // EXTI->RTENR = EXTI_RTENR_TR0; // Trigger ansteigende Flanke auf Line 0, RTE = rising trigger edge NVIC_EnableIRQ( EXTI7_0_IRQn ); // enable Interrupt } /* -------------------------------------------------------- EXTI7_0_IRQ Interruptvektor Interrupthandler fuer externen Interrupt, hier PinChange auf Line 0 -------------------------------------------------------- */ void EXTI7_0_IRQHandler( void ) __attribute__((interrupt)); void EXTI7_0_IRQHandler( void ) { static uint8_t anzcx = 0; // NVIC_DisableIRQ( EXTI7_0_IRQn ); // Interrupt abschalten if (anzcx== 0) { anzcx++; counter32= 0; } else { frequency= 92295000 / counter32; counter32= 0; anzcx= 0; newval= 1; } EXTI->INTFR = EXTI_Line0; // Interrupt quittieren // NVIC_EnableIRQ( EXTI7_0_IRQn ); // Interrupt wieder zulassen } /* -------------------------------------------------------- monitor_getchar liest ein Zeichen aus dem Ringspeicher. Ist keines eingetroffen, wird auf den Eingang eines Zeichens auf der USART gewartet. -------------------------------------------------------- */ uint8_t monitor_getchar(void) { uint8_t ch = 0; while ((recv_buf_current == recv_buf_next) && ( !(is_but()) )); if (is_but()) { return 0; } if (recv_buf_current != recv_buf_next) { ch = recv_buf[recv_buf_current]; recv_buf_current= (recv_buf_current + 1) % RECV_BUF_SIZE; } return ch; } /* -------------------------------------------------------- my_putchar wird von my-printf / printf aufgerufen -------------------------------------------------------- */ void my_putchar(char ch) { static uint8_t cmdargs[10]; static uint8_t cmdanz=1; static uint8_t cmd; int xp; uint8_t i; char tch; char *s; static uint8_t intens= 0; if ((ch == 27) || (cmd > 0)) { if (ch == 27) { cmd= 1; } else { if ((cmd== 1) && (ch= '[')) { cmd= 2; // es wurde \033[ gefunden } else { if ((ch >= '0') && (ch <= '9')) // ist es ein Argument ? { cmdargs[cmdanz-1]= (cmdargs[cmdanz-1]*10) + ch-'0'; // dann Argument-Ascii to Char } else if (ch== ';') // weiteres Argument { cmdanz++; } else if (ch== 'J') { if (cmdargs[0]== 2) { clrscr(); fb_clear(); wherex= 0; wherey= 0; gotoxy(wherex, wherey); } for (i= 0; i< 10; i++) cmdargs[i]= 0; cmdanz= 1; cmd= 0; } // Cursorpositionierung else if (ch== 'H') { i= 0; if (cmdanz>1) // es gab Parameter { while (i< cmdanz) { switch (i) { case 0 : { if (cmdargs[0]-1 < yt_max) { wherey= cmdargs[0] - 1; gotoxy(wherex,wherey); } break; } case 1 : { wherex= cmdargs[1] - 1; gotoxy(wherex,wherey); break; } default : break; } i++; } } else // /033[H = keine Paramter => gotoxy(0,0); { wherex= 0; wherey= 0; gotoxy(wherex, wherey); } for (i= 0; i< 10; i++) cmdargs[i]= 0; cmdanz= 1; cmd= 0; } // Farbeinstellungen else if (ch== 'm') { i= 0; while (i< cmdanz) { switch (cmdargs[i]) { case 0 : intens= 0; break; case 1 : intens= 1; break; case 30 : textcolor= (0 + (intens*8)); break; // sw case 31 : textcolor= (4 + (intens*8)); break; // rt case 32 : textcolor= (2 + (intens*8)); break; // gn case 33 : textcolor= (6 + (intens*8)); break; // br case 34 : textcolor= (1 + (intens*8)); break; // bl case 35 : textcolor= (5 + (intens*8)); break; // mg case 36 : textcolor= (3 + (intens*8)); break; // cy case 37 : textcolor= (7 + (intens*8)); break; // gr case 40 : bkcolor= 0; break; // sw case 41 : bkcolor= 4; break; // rt case 42 : bkcolor= 2; break; // gn case 43 : bkcolor= 6; break; // br case 44 : bkcolor= 1; break; // bl case 45 : bkcolor= 5; break; // mg case 46 : bkcolor= 3; break; // cy case 47 : bkcolor= 7; break; // gr case 49 : textcolor= 7; bkcolor= 0; intens= 0; break; default : break; } i++; } for (i= 0; i< 10; i++) cmdargs[i]= 0; cmdanz= 1; cmd= 0; } else { cmdanz= 1; cmd= 0; } } } } else { switch (ch) { // linefeed case 0x0a: { if (wherey == yt_max-1) { fb_scrollup(); } else { wherey++; gotoxy(wherex, wherey); } break; } // carriage return case 0x0d: { wherex= 0; gotoxy(wherex, wherey); break; } // bs, backspace case 0x08: { gotoxy(wherex-1, wherey); break; } case 0x7f: { gotoxy(wherex-1, wherey); break; } // ht, horizontal tab, Anzahl Zeichen zwischen den TABs siehe // tft_termdisplay_v3.h case 0x09 : { xp= wherex; if (xp < (xt_max-tablength)) { xp= xp / tablength; xp++; xp= xp * tablength; } wherex= xp; gotoxy(wherex, wherey); break; } // dle, data link escape, hier einlesen eines Turtelgrafikstrings case 0x10 : { s= &turtlestring[0]; do { tch= monitor_getchar(); if (is_but()) { return; } if ((tch != 0) && (tch != 0x0a) && (tch != 0x0d) && (tch != 0x17)) { *s= tch; s++; } } while((tch != 0) && (tch != 0x0a) && (tch != 0x0d) && (tch != 0x17)); *s= 0; turtle_graph(&turtlestring[0]); break; } default : { if (wherex < (xt_max)) { lcd_putchar(ch); fb_adr= (wherex * 2) + (wherey * (xt_max * 2)); // Adresse des Attributs & Zeichen im Framebuffer fb[fb_adr]= (bkcolor << 4) | (textcolor & 0x0f); fb[fb_adr+1]= ch; // Zeichen und Attribut im Framebuffer ablegen wherex++; } break; } } } } void settextattr(uint8_t attr) { bkcolor= (attr >> 4); textcolor= attr & 0x0f; } /* -------------------------------------------------------- picomon_proc serielles Terminalprogramm 4800 bd 8N1. Terminal kann nur empfangen, nicht senden -------------------------------------------------------- */ void picomon_prog(void) { char ch; uart_init(BAUD); setfont(fnt12x16); lcd_orientation(tft_ausrichtung); textsize= 0; fb_clear(); gotoxy(1,1); printf(" "); recv_buf_current= recv_buf_next; clrscr(); // und Framebuffer des Displays loeschen printf("\033[2J\033[H"); printf("\033[49;1;33;44m"); // gelbe Schrift, blauer Hintergrund printf(" --PicoMon-- \n\r"); settextattr(0x07); NVIC_EnableIRQ(USART1_IRQn); // Interrupts fuer USART1 zulassen USART1->CTLR1 |= USART_CTLR1_RXNEIE; // Flag fuer Receive-Interrupt zulassen while( !(is_but()) ) { ch= monitor_getchar(); if (!is_but()) { my_putchar(ch); } } NVIC_DisableIRQ(USART1_IRQn); // Interrupts fuer USART1 sperren delay(50); while(is_but()); delay(50); } /* -------------------------------------------------------- fcount_prog einfacher, reziproger Frequenzzaehler. Getestet bis 45 kHz. (sicherlich viel "Luft nach oben" fuer Optimierungen -------------------------------------------------------- */ void fcount_prog(void) { NVIC_DisableIRQ(USART1_IRQn); // Interrupts fuer USART1 sperren pd0_pinchange_init(); timer2_init(); setfont(fnt12x16); lcd_orientation(tft_ausrichtung); textsize= 0; fb_clear(); settextattr(0x07); clrscr(); // und Framebuffer des Displays loeschen gotoxy(0,0); printf(" "); settextattr(0x1f); gotoxy(0,1); // printf(" \n\r"); printf(" F-Counter "); // printf(" "); settextattr(0x0a); gotoxy(1,3); printf("f: 0"); settextattr(0x0e); while( !(is_but()) ) { if (newval) { gotoxy(4,3); if (frequency < 10000) { printfkomma= 2; printf("%k Hz ", frequency); newval= 0; } else { if (frequency < 100000) { printfkomma= 1; printf("%k Hz ", frequency / 10); newval= 0; } else { printfkomma= 2; printf("%k KHz ", frequency / 1000); newval= 0; } } } delay(250); } NVIC_DisableIRQ( EXTI7_0_IRQn ); // ext. Interrupt abschalten NVIC_DisableIRQ(TIM2_IRQn); // Timerinterrupt abschalten delay(50); while(is_but()); delay(50); settextattr(0x07); clrscr(); } /* -------------------------------------------------------- main -------------------------------------------------------- */ int main(void) { // uint8_t ch; SystemInit(); // 48 MHz intern, Clock fuer PORTA, PORTC, PORTD eingeschaltet but_init(); lcd_init(); // Display initialisieren while(1) { switch (progrun) { case 0: picomon_prog(); break; case 1: fcount_prog(); break; default: break; } progrun++; progrun %= 2; } fb_clear(); }