/* serielle Kommunikation für STM32F103C8T6 */ #include "StdTypes.h" #include "STM32F103all.h" #include "startup.h" #include "config.h" #include "serial.h" /* Funktionsprinzip: Der eigentliche Datenverkehr über die diversen UART's wird weitgehend interruptgesteuert erledigt. Als Schnittstelle zu den Anwendungen dienen Sende- und Empfangspuffer, die mit entsprechenden Funktionen abgefragt und befüllt werden können. */ /* Pufferlängen: immer eine 2er Potenz */ #define OBLEN 32 #define IBLEN 8 #define TUBUF struct T_UARTBUF struct T_UARTBUF { char OutBuf[OBLEN]; char InBuf[IBLEN]; volatile int InWP; volatile int InRP; volatile int OutWP; volatile int OutRP; }; /* die Puffer für alle 3 USART */ TUBUF U1Buf; TUBUF U2Buf; TUBUF U3Buf; TUBUF* GetUsartBuffer (int uart) { switch(uart) { case 1: return &U1Buf; case 2: return &U2Buf; case 3: return &U3Buf; } return 0; } /* Interrupt-Nummern */ #define U1Int 37 #define U2Int 38 #define U3Int 39 /************** Interrupt-Handler ****************/ /* merke: die Interrupt-Bits in USARTx_ISR sind statisch! also erzeugen sie SOFORT einen neuen Interrupt, wenn man sie nach dem letzten gesendeten Byte nicht per USARTx_CR1 ausschaltet. */ __irq void USART1_IRQHandler (void) { char c; int i, j; if (USART1_ISR & ((1<<5)|(1<<3))) // RX: Zeichen empfangen oder Overrun { c = USART1_RDR; i = U1Buf.InWP; j = (i+1) & (IBLEN-1); if (j!=U1Buf.InRP) { U1Buf.InBuf[i] = c; U1Buf.InWP = j; } } if (USART1_ISR & (1<<7)) // TX: Sendepuffer leer geworden { i = U1Buf.OutRP; if (i!=U1Buf.OutWP) // ob es was zu senden gibt { USART1_TDR = U1Buf.OutBuf[i]; U1Buf.OutRP = (i+1) & (OBLEN-1); } else { USART1_CR1 &= ~(3<<6); // nö, TXEIE und TCIE ausschalten } } } __irq void USART2_IRQHandler (void) { char c; int i, j; if (USART2_ISR & ((1<<5)|(1<<3))) // RX: Zeichen empfangen { c = USART2_RDR; i = U2Buf.InWP; j = (i+1) & (IBLEN-1); if (j!=U2Buf.InRP) { U2Buf.InBuf[i] = c; U2Buf.InWP = j; } } if (USART2_ISR & (1<<7)) // TX: Sendepuffer leer? { i = U2Buf.OutRP; if (i!=U2Buf.OutWP) // ob es was zu senden gibt { USART2_TDR = U2Buf.OutBuf[i]; U2Buf.OutRP = (i+1) & (OBLEN-1); } else { USART2_CR1 &= ~(3<<6); // nö, TXEIE und TCIE ausschalten } } } __irq void USART3_IRQHandler (void) { char c; int i, j; if (USART3_ISR & ((1<<5)|(1<<3))) // RX: Zeichen empfangen { c = USART3_RDR; i = U3Buf.InWP; j = (i+1) & (IBLEN-1); if (j!=U3Buf.InRP) { U3Buf.InBuf[i] = c; U3Buf.InWP = j; } } if (USART3_ISR & (1<<7)) // TX: Sendepuffer leer? { i = U3Buf.OutRP; if (i!=U3Buf.OutWP) // ob es was zu senden gibt { USART3_TDR = U3Buf.OutBuf[i]; U3Buf.OutRP = (i+1) & (OBLEN-1); } else { USART3_CR1 &= ~(3<<6); // nö, TXEIE und TCIE ausschalten } } } /************* USART/UART aufsetzen **************************/ /* Baudrate errechnen: Die Faustformel ist: Baudrate = Clock / BRR; Clock ist hier 72 MHz, da SysClock als Taktquelle gewählt. BRR ist eigentlich eine 16 Bit Festkommazahl: die untersten 4 Bit sind der gebrochene Anteil Formel lt. Manual Baud = 36MHz / (16*BRR); aber da im BRR Register die BRR ja ohnehin mit 4 Nachkommabits steht, ist sie bereits an der richtigen Stelle und BRR_value = 36MHz / Baudrate; ist bereits die richtige Formel. Voraussetzung: U(S)ART muß in RCC_APBxENR bereits freigegeben sein Return: tatsächliche Baudrate */ dword InitSerial1 (long baudrate) /* liefert tatsächliche Baudrate zurück */ { long Baudteiler; Baudteiler = F_APB / baudrate; /* SysClk Frequenz */ U1Buf.InWP = /* alle Puffer rücksetzen */ U1Buf.InRP = U1Buf.OutWP = U1Buf.OutRP = 0; /* Int 37 */ NVIC_ICPR1 = (1<<(U1Int-32)); /* pending löschen */ NVIC_ISER1 = (1<<(U1Int-32)); /* Int erlauben */ USART1_CR1 = 0; /* erstmal aus */ USART1_CR2 = (3<<12); /* 1.5 Stopbits */ USART1_CR3 = 0; USART1_GTPR = 0; USART1_BRR = Baudteiler & 0xFFFF; /* Baudratenteiler */ USART1_CR1 = (1<<13) | /* USART enable */ (1<<7) | /* TX Int enable */ (1<<5) | /* RX Int enable */ (1<<3) | /* TX enable */ (1<<2); /* RX enable */ return F_APB / Baudteiler; } dword InitSerial2 (long baudrate) /* liefert tatsächliche Baudrate zurück */ { long Baudteiler; Baudteiler = F_APB / baudrate; /* SysClk Frequenz */ U2Buf.InWP = /* alle Puffer rücksetzen */ U2Buf.InRP = U2Buf.OutWP = U2Buf.OutRP = 0; /* Int 38 */ NVIC_ICPR1 = (1<<(U2Int-32)); /* pending löschen */ NVIC_ISER1 = (1<<(U2Int-32)); /* Int erlauben */ USART2_CR1 = 0; /* erstmal aus */ USART2_CR2 = (3<<12); /* 1.5 Stopbits */ USART2_CR3 = 0; USART2_GTPR = 0; USART2_BRR = Baudteiler & 0xFFFF; /* Baudratenteiler */ USART2_CR1 = (1<<13) | /* USART enable */ (1<<7) | /* TX Int enable */ (1<<5) | /* RX Int enable */ (1<<3) | /* TX enable */ (1<<2); /* RX enable */ return F_APB / Baudteiler; } dword InitSerial3 (long baudrate) /* liefert tatsächliche Baudrate zurück */ { long Baudteiler; Baudteiler = F_APB / baudrate; /* SysClk Frequenz */ U3Buf.InWP = /* alle Puffer rücksetzen */ U3Buf.InRP = U3Buf.OutWP = U3Buf.OutRP = 0; /* Int 39 */ NVIC_ICPR1 = (1<<(U3Int-32)); /* pending löschen */ NVIC_ISER1 = (1<<(U3Int-32)); /* Int erlauben */ USART3_CR1 = 0; /* erstmal aus */ USART3_CR2 = (3<<12); /* 1.5 Stopbits */ USART3_CR3 = 0; USART3_GTPR = 0; USART3_BRR = Baudteiler & 0xFFFF; /* Baudratenteiler */ USART3_CR1 = (1<<13) | /* USART enable */ (1<<7) | /* TX Int enable */ (1<<5) | /* RX Int enable */ (1<<3) | /* TX enable */ (1<<2); /* RX enable */ return F_APB / Baudteiler; } /****************** I/O Funktionen *******************************/ /* ob empfangene Zeichen vorhanden sind */ bool V24RxAvail1 (void) { if (U1Buf.InRP != U1Buf.InWP) return 1; return 0; } bool V24RxAvail2 (void) { if (U2Buf.InRP != U2Buf.InWP) return 1; return 0; } bool V24RxAvail3 (void) { if (U3Buf.InRP != U3Buf.InWP) return 1; return 0; } /* empfangene Zeichen abholen */ char V24GetChar1 (void) { int i; char c; i = U1Buf.InRP; if (i == U1Buf.InWP) return 0; c = U1Buf.InBuf[i]; U1Buf.InRP = (i+1) & (IBLEN-1); return c; } char V24GetChar2 (void) { int i; char c; i = U2Buf.InRP; if (i == U2Buf.InWP) return 0; c = U2Buf.InBuf[i]; U2Buf.InRP = (i+1) & (IBLEN-1); return c; } char V24GetChar3 (void) { int i; char c; i = U3Buf.InRP; if (i == U3Buf.InWP) return 0; c = U3Buf.InBuf[i]; U3Buf.InRP = (i+1) & (IBLEN-1); return c; } /* Anzahl freier Plätze im Sendepuffer ermitteln */ int V24numTxFree1 (void) { int i; i = U1Buf.OutWP - U1Buf.OutRP; if (i<0) i = i + OBLEN; /* i = belegte Plätze */ return OBLEN - i; } int V24numTxFree2 (void) { int i; i = U2Buf.OutWP - U2Buf.OutRP; if (i<0) i = i + OBLEN; /* i = belegte Plätze */ return OBLEN - i; } int V24numTxFree3 (void) { int i; i = U3Buf.OutWP - U3Buf.OutRP; if (i<0) i = i + OBLEN; /* i = belegte Plätze */ return OBLEN - i; } /* Zeichen senden */ char V24Char_Out1 (char c) { long L; int i, j; i = U1Buf.OutWP; j = (i+1) & (OBLEN-1); L = Ticks + 100; while (j == U1Buf.OutRP); // wenn Puffer voll { if (L < Ticks) return 0; // Timeout } // Puffer nicht voll, also Zeichen einstapeln U1Buf.OutBuf[i] = c; U1Buf.OutWP = j; USART1_CR1 |= (1<<7); // Sendeinterrupt einschalten return c; } char V24Char_Out2 (char c) { long L; int i, j; i = U2Buf.OutWP; j = (i+1) & (OBLEN-1); L = Ticks + 100; while (j == U2Buf.OutRP); // wenn Puffer voll { if (L < Ticks) return 0; // Timeout } // Puffer nicht voll, also Zeichen einstapeln U2Buf.OutBuf[i] = c; U2Buf.OutWP = j; USART2_CR1 |= (1<<7); // Sendeinterrupt einschalten return c; } char V24Char_Out3 (char c) { long L; int i, j; i = U3Buf.OutWP; j = (i+1) & (OBLEN-1); L = Ticks + 100; while (j == U3Buf.OutRP); // wenn Puffer voll { if (L < Ticks) return 0; // Timeout } // Puffer nicht voll, also Zeichen einstapeln U3Buf.OutBuf[i] = c; U3Buf.OutWP = j; USART3_CR1 |= (1<<7); // Sendeinterrupt einschalten return c; } /* Warten, bis alle Zeichen gesendet sind */ void V24TxDone1 (void) { while (U1Buf.OutRP != U1Buf.OutWP);} void V24TxDone2 (void) { while (U2Buf.OutRP != U2Buf.OutWP);} void V24TxDone3 (void) { while (U3Buf.OutRP != U3Buf.OutWP);} /**** ende ****/