Hier sollen daher nur Programmierbeispiele für die STM32-Serie stehen die auch funktionieren UND getestet wurden.
- Digital IO
- Serial IO
- Analog IO
- CAN Bus IO
Zur Programmierumgebung IDEs und deren Einrichtung gibts genügend Infos im Web, daher verzichte ich hier mal darauf.
Hier ist eine erste Anlaufstelle.
und hier ist noch eine: Setting up Eclipse and Code Sourcery lite for STM32 Discovery Development
Die Beispiele beziehen sich auf das obige Demo Board von Olimex STM32P107. Aufgrund der gleichen Architektur der Cortex-Serie können die Beispiele auch leicht für andere CPUs aus der gleichen Serie sogar von anderen Herstellern verwendet werden ohne dass man groß Änderungen vornehmen muss. Dies ist in meinen Augen der größte Vorteil gegenüber den AVRs von ATMEL, wo man bei einem CPU Wechsel jedesmal alle Register etc. neu anpassen muss - soweit zumindest die Theorie. Die gelisteten Programme sind so aufgebaut, dass quasi alle Quellen in einem File enthalten sind damit dies im Webbrowser besser zu lesen ist. Ich empfehle jedoch die Aufteilung der Quellen in Header- und Libraryfiles, sodass man die Routinen leicht wiederverwenden kann und man auch eine bessere Übersichtlichkeit bekommt.
Voraussetzungen:
- Windows PC mit Betriebssystem Windows 7,
- Programmierumgebung: Eclipse basierte IDE (CooCox CoIDE) in der Version: 1.6.2
- Programmieradapter: ST-LINK/V2
- Olimex-Boards STM32-P107 und Olimexino STM31
Digital IO
Einfaches Testprogramm - das Hello World der Elektroniker - um 2 LEDS blinken zu lassen. Zwar einfach aber als Test um die Toolchain und Programmierhardware etc. zu testen völlig ausreichend.
/* Wolfram Koerver * STM32 Example for OLIMEX STM32P107 Test Board * Toggle green and yellow LEDs Test Program this is the "hello world for electronics" * 1.1.2013 */ #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" void init_GPIO(void); void delay_nus(uint32_t nus); int main(void) { init_GPIO(); while(1) { /* Toggle LEDs which connected to PC6 and PC7 ; Green/Yellow LEDs from Olimex Board*/ GPIOC->ODR ^= GPIO_Pin_6; // invert pin delay_nus (100000); // delay a short time GPIOC->ODR ^= GPIO_Pin_7; // toggle other LED delay_nus (100000); } } void init_GPIO(void) { /* Initialize GPIO Structure, configure clock on peripheral bus * Enable GPIO Pins for LED which are connected to PC6,7*/ /* Configure the GPIO_LED pin PC6 = Green LED PC7 = Yellow LED * Configure direction and clock speed*/ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } /***************************************************************************//** * @brief delay in n us * @param[in] n:delay value * @return None *******************************************************************************/ void delay_nus(uint32_t nus) { uint32_t temp; SysTick->LOAD = nus * 11; SysTick->VAL = 0x00; SysTick->CTRL = 0x01 ; do { temp=SysTick->CTRL; }while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL = 0x00; SysTick->VAL = 0X00; }
Nachfolgend noch der Download link für das Demoprogramm.
Wie man die einzelnen Bits oder Ports anspricht findet sich hier.
SERIAL IO
Wie spricht man die USART Schnittstellen des STM32 an ?
Für die Cortex CPU Serie hat ST viele Funktionen auf einem Chip integriert mit dem Problem: Wie verwendet man was und wann ? Da ist eine Schnittstelle in die CPU hinein recht hilfreich.
Spätestens wenn man die ersten Schritte in der Programmierung mit der neuen CPU macht, muss man mitunter auch Debug-Infos etc. ausgeben können. Dies ist im einfachsten Fall per ST LINK und Debugger leicht möglich, aber manchmal will man auch einfach aktuelle Werte von Variablen oder ADC-Werte zum Test kontinuierlich ausgeben können. In diesem Fall muss man zunächst eine serielle Schnittstelle einrichten die die Ausgabe übernimmt. Diese Schnittstelle kann man dann mit einem Terminalprogramm vom PC aus bedienen.
Nachfolgend ist ein einfaches Testprogramm um die USART Schnittstellen 1 und 2 anzusprechen.
#include <stdio.h> #include "stm32f10x_usart.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include <stddef.h> #include "stm32f10x.h" #include "stm32f10x_conf.h" // Function prototypes void InitUart1(void); void InitUart2(void); void RCC_Init(void); void USART1_SendString(char* data); void USART2_SendString(char* data); void delay_nus(uint32_t nus); /***************************************************************************//** * @brief delay some time ;n us * @param[in] n:delay value * @return None *******************************************************************************/ void delay_nus(uint32_t nus) { uint32_t temp; SysTick->LOAD = nus * 11; SysTick->VAL = 0x00; SysTick->CTRL = 0x01 ; do { temp=SysTick->CTRL; }while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL = 0x00; SysTick->VAL = 0X00; } void RCC_Init(void) { // Route and enable clocks for low and high speed bus RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); } void InitUart1(void) { // Create Structs GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; // USART1 RX-Pin initialize GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOA, &GPIO_InitStructure); // USART1 TX-Pin initialize GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_Init(GPIOA, &GPIO_InitStructure); // USART and clock initialize USART_ClockStructInit(&USART_ClockInitStructure); USART_ClockInit(USART1, &USART_ClockInitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } void InitUart2(void) { // Initialize Structs GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; // USART1 RX-Pin initialize PD6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOD, &GPIO_InitStructure); // USART1 TX-Pin initialize PD5 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_Init(GPIOD, &GPIO_InitStructure); /* Remap USART, as USART2 is used as alternate pins on PD5/6*/ GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); // USART initialize USART_ClockStructInit(&USART_ClockInitStructure); USART_ClockInit(USART2, &USART_ClockInitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE); } void USART1_SendString(char* data) { while (*data) { USART_SendData(USART1, (uint16_t) *data); while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); data++; } } void USART2_SendString(char* data) { while (*data) { USART_SendData(USART2, (uint16_t) *data); while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET); data++; } } // ******************************************************************* // ***************************** Main ******************************** // Wolfram 2012 - Test program for USART IO // ******************************************************************* // ***************************** Main ******************************** int main(void) { SystemInit(); RCC_Init(); InitUart1(); InitUart2(); while(1) { delay_nus(100000); USART1_SendString("First String\n"); while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); delay_nus(100000); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); USART2_SendString("Second String\n\r"); } }
Im obigen Beispiel wird der USART1 und USART2 angesprochen.
Achtung, nicht einfach die USART Nummern umbenennen wenn ein anderer USART verwendet werden soll - das geht ohne Studium der Chip-Doku schief, da viele Register miteinander in logischer Verbindung stehen.
Auch ist es ratsam bei der Programmierung die Interruptprogrammierung in EINER Routine unterzubringen, statt je Interruptquelle in separaten Routinen. Sonst programmiern sich diese gegenseitig die Register um und man wundert sich warum ein Interrupt partout nicht kommt.
Die Pinbelegung ist :
USART1 : PA9 (Tx) und PA10(Rx) auf Port A
USART2 : PA5 (Tx) und PA6 (Rx) auf Port D
Achtung diese Anschlüsse sind die direkten CPU-Anschlüsse, die müssen über passende TTL/RS232 Wandler angeschlossen werden.
Auf dem Olimex Board STM32-P107 ist der UART2 direkt über einen Wandler auf eine 9-polige Buchse herausgeführt. Da ist natürlich kein Wandler mehr erforderlich.
Hier ist das Programm als Downloadlink:
Timer & Interrupts
Im richtigen "Leben" eines Microcontrollerprogramms benötigt man eigentlich immer genaue Zeiten. Damit ein Prozessor auch zwischendurch quasi andere Dinge erledigen kann (und auch muss) sind Interrupts am besten geeignet. Nachfolgend ist ein Programm welches den Timer TIM2 so programmiert, dass alle 1ms ein Interrupt ausgelöst wird.
#include <stdio.h> #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include <stddef.h> #include "stm32f10x.h" #include "stm32f10x_conf.h" #include <string.h> #include <misc.h> /* * Program to test the TIM2 timer and interrupt function for STM32F107VC. * tested with OLIMEX STM32-P107 board. * Wolfram Koerver - 5.1.2012 * Program starts blinking with approx 1/sec with the yellow LED on port PC7 * Interrupt is generated every 500us to flip the green LED on/off on port PC6 */ void init_LED(void); void blink_yellow(void); void blink_green(void); void toggle_green(void); void init_timer(void); void init_DO(void); void toggle_do(void); void toggle_dot(void); void delay_us(uint32_t time_us); void delay_ms(uint32_t time_ms); void TIM2_IRQHandler(void); void init_sys_tick(void); int main(void) { SystemInit(); // initialize the core init_LED(); // Init the LED port IOs init_DO(); // init digital output ports for digital signal init_timer(); // configure the Timer Interrupt while(1) { toggle_green(); //blink_yellow(); // let the main program do something delay_ms(500); } } void TIM2_IRQHandler(void){ // Timer Interrupt handler for TIM2 will be called every 1ms TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // Clear interrupt pending bit //toggle_green(); // just toggle the output port bit toggle_dot(); //generate output bit pulse on output } void init_timer(void) { // SystemCoreClock = 72MHz -> APB1 is maximum 36MHz for TIM2,3,4,5 but will be doubled again to 72MHz when prescaler # 0 // This example uses TIM2 Timer // APB1 clock = SystemCoreClock/2 but s.o. TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBase_InitStructure.TIM_Period = 999; // count up to 999 = 1000 TIM_TimeBase_InitStructure.TIM_Prescaler = 71; //72(MHz) -1 Divided by 72 TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; // // 1MHz / 500 = 2000 Hz. // SystemCoreClock = 72MHz / 72 => 1 MHz Clock with this clock the timer counts up to 999 = 1000 counts = // 1/1000 --> 1000 Hz = 1ms cycle time TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM2, ENABLE); } void delay_us(uint32_t nus) { uint32_t temp; SysTick->LOAD = nus * 11; SysTick->VAL = 0x00; SysTick->CTRL = 0x01 ; do { temp=SysTick->CTRL; }while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL = 0x00; SysTick->VAL = 0X00; } void delay_ms(uint32_t time_ms) //Delay in msec { while (time_ms>0) { delay_us(1000); time_ms--; } } void init_DO(void) { /* * the following statements are for the DO Output Pins to measure * Clock settings. Initialize GPIO Structure, configure clock on peripheral bus * Enable GPIO Pins PD8 * Configure direction and clock speed */ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); } void init_LED(void) { // the following statements are for the LEDs /* Initialize GPIO Structure, configure clock on peripheral bus * Enable GPIO Pins for LED which are connected to PC6,7*/ /* Configure the GPIO_LED pin PC6 = Green LED PC7 = Yellow LED * Configure direction and clock speed*/ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } void blink_yellow() { GPIOC->BRR = GPIO_Pin_7; //reset bit GPIOC->BSRR = GPIO_Pin_7; //set bit //delay_nus(30000); delay_ms(1); GPIOC->BRR = GPIO_Pin_7; //reset bit //GPIOC->ODR ^= GPIO_Pin_7; // invert pin } void blink_green() { GPIOC->BRR = GPIO_Pin_6; //reset bit GPIOC->BSRR = GPIO_Pin_6; //set bit delay_ms(1); GPIOC->BRR = GPIO_Pin_6; //reset bit //GPIOC->ODR ^= GPIO_Pin_6; // invert pin } void toggle_green() { GPIOC->ODR ^= GPIO_Pin_6; // invert pin } void toggle_yellow() { GPIOC->ODR ^= GPIO_Pin_7; // invert pin } void toggle_do() { GPIOD->ODR ^= GPIO_Pin_8; // invert pin } void toggle_dot() { GPIOD->BSRR = GPIO_Pin_8; //set bit delay_us(1); GPIOD->BRR = GPIO_Pin_8; //reset bit }
Hierzu wird dann in der Interruptroutine ein Hardwareausgang getoggelt. Im Interrupt-handler void TIM2_IRQHandler(void) kann man dann z.B. zeitbezogene Aktionen ausführen. Hier sollten dann natürlich nur kurze Funktionen eingebaut werden, wie z.B. einlesen von digitalen Werten der IO Ports oder zum Beispiel Entprellfunktionen etc.. In der Hauptschleife des Programms wird eine LED zum Blinken gebracht. Eine delay Funktion auf Basis des SysTicks sorgt für ein Blinken im Sekundentakt; hierzu muss daher kein extra Timer verschwendet werden.
Hier ist das Programm als Downloadlink:
CAN Communication mit STM32
Um mit dem CAN Bus zu kommunizieren benötigt man natürlich die Angabe der Bitrate. Wenn die einmal bekannt ist, muss man den STM32 eben nur noch auf die passende Rate einstellen. Hierzu dienen diverse Register. Um die Registereinstelllungen nicht jedesmal neu zu berechnen folgt hier eine Tabelle mit den Einstellungen: (Angaben sind für das Olimexino Board und dem Prozessor STM32F103RB bei eine APB1 Takt von 36MHz).
APB1=36MHz | |||
Baudrate kBit/s |
CAN_Prescaler |
CAN_BS1_ Register |
CAN_BS2_ Register |
2000 |
1 | 12 | 5 |
1000 | 2 | 12 | 5 |
500 | 4 | 12 | 5 |
400 | 5 | 12 | 5 |
250 | 8 | 12 | 5 |
200 | 10 | 12 | 5 |
125 | 16 | 12 | 5 |
100 | 20 | 12 | 5 |
50 | 40 | 12 | 5 |
25 | 80 | 12 | 5 |
10 | 200 | 12 | 5 |
Oder direkt als C code:
... CAN_StructInit(&CAN_InitStructure); CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = DISABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = DISABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; //CAN_InitStructure.CAN_Prescaler=1; // 2000kbit/s //CAN_InitStructure.CAN_Prescaler=2; // 1000kbit/s CAN_InitStructure.CAN_Prescaler=4; // 500kbit/s //CAN_InitStructure.CAN_Prescaler=5; // 400kbit/s //CAN_InitStructure.CAN_Prescaler=8; // 250kbit/s //CAN_InitStructure.CAN_Prescaler=10; // 200kbit/s //CAN_InitStructure.CAN_Prescaler=16; // 125kbit/s //CAN_InitStructure.CAN_Prescaler=20; // 100kbit/s //CAN_InitStructure.CAN_Prescaler=40; // 50kbit/s //CAN_InitStructure.CAN_Prescaler=80; // 40kbit/s //CAN_InitStructure.CAN_Prescaler=200; // 10kbit/s
Beispielprogramm zu vollständigen CAN-Kommunikation findet sich in diesem Beitrag.
TSIC Temperaturerfassung
Hier folgt jetzt ein kleines STM32 Testprogram welches einen TSIC-Temperatursensor ausliest. Zusätzlich wird noch ein Taster auf dem Demoboard abgefragt und eine LED zum blinken gebracht. Das Beispiel wurde für das STM-32-P107 Demoboard geschrieben. Die Kernroutine zum lesen der Temperatur ist: readTSicTemp. Über die serielle Schnittstelle wird der aktuelle Temperaturwert dann ausgegeben.
Achtung nicht vergessen:
Damit sprintf Funktionen für die UART-Ausgaberoutinen einwandfrei funktionieren muss man folgendes beachten:
1. Erhöhung der stack size
Dies erfolgt im File startup_stm32f10x_cl.c dort sind nur 200 bytes also 100 Worte vorgesehen.
#define STACK_SIZE 0x00000100 /*!< Stack size (in Worten) */
ersetzen durch
#define STACK_SIZE 0x00002000
(das gibt dann wenigstens 4kB) sowie im File:
(void *)&pulStack[STACK_SIZE-1], ersetzen durch
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
ersetzen. Sofern größere Texte zu verarbeiten sind sollte man die Größe noch weiter erhöhen. Es gibt ja genug ;-)
Wie spricht man denn jetzt TSIC Sensoren an ? Nun - eigentlich muss man nichts tun außer auf die erste fallende Flanke der Datenleitung zu warten, dann im Abstand der Taktrate die Bits einzeln einlesen. Eigentlich recht einfach. Nachteil ist, man weiß nicht WANN denn der Sensor die Daten sendet. Die Daten werden alle 100ms ausgesendet, d.h. man muss bis zu 100ms warten bevor man wieder den Beginn des Telegramms erkennen kann. Daher hat die Routine auch einen Timeoutparameter den man dann am besten auf 150ms setzt. falls kein Sensor angeschlossen ist, wird ein Fehler zurückgegeben - nach der Timeoutzeit. Der Sensor wird quasi nur mit 5V versorgt und die Datenausgangsleitung an GPIOD - PIN1 angeschlossen. Hier gibts übrigens Informationen zum verwendeten Sensor von ist-ag. Die Routinen wären sicherlich einfacher gewesen, wenn man diese über Interruptfunktionen implementiert hätte. Ich wollte aber für eine solche Sekundärfunktion keinen Interruptpin verschwenden, zumal sich Temperaturen ohnehin nur langsam ändern.
/* *********************************************************************************************** * * This program is a test program to read a TSIC temperature sensor TSIC 306 connected to the OLIMEX STM32-P107 Board. * Connected to GPIOD - PIN1; Button is read and green LED does blink * Wolfram Koerver, 2012 * * ***********************************************************************************************/ #include <stdio.h> #include "stm32f10x_usart.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include <stddef.h> #include "stm32f10x.h" #include "stm32f10x_conf.h" #include <string.h> #include <misc.h> #include <stdlib.h> /* function prototypes */ void GPIO_Configuration(void); void NVIC_Configuration(void); void USART1_IRQHandler(void); void USART1_SendString(char* data); void USART2_SendString(char* data); void delay_nus(uint32_t nus); void Init_Led(void); void blink_yellow(void); void blink_green(void); void InitUart2(void); void USART2_SendString(char* data); void USART_puts(USART_TypeDef* USARTx, volatile char *s); void Init_TSICPort(void); unsigned int read_sw(void); void RCC_Init(void); uint8_t readTSicTemp (uint16_t *temp_value16, uint8_t channel, uint32_t timeout); //Interrupt handler declaration void USART2_IRQHandler(); char bufferRx[127],receiveRx[127]; int RxCounter,printRx,switch_port; int main(void) { uint16_t temperatur; uint8_t returnvalue; // return value of getTSicTemp(*temp); float grad; char out_text[50]; SystemInit(); RCC_Init(); NVIC_Configuration(); Init_Led(); InitUart2(); Init_TSICPort(); USART_ITConfig(USART1, USART_IT_TXE, DISABLE); /* Enabling interrupt from USART2 */ USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART2_SendString("Program Start\n\r"); //while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); while(1) { delay_nus(500000); // wait 0.5s blink_yellow(); // blink yellow LED every 500ms returnvalue = readTSicTemp(&temperatur,1,50000); // fetch TSIC temp out_text[49] = '\0'; // make sure string termination if (returnvalue == 0){ // conversion to temperature grad = ((float)temperatur / 2047 * 200) - 50; out_text[49] = '\0'; sprintf(out_text,"TSIC %4.5f",grad); USART_puts(USART2,out_text); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); USART_puts(USART2,"deg C "); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); sprintf(out_text,"TSICRAW %6d \r",temperatur); USART_puts(USART2,out_text); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); } else { sprintf(out_text,"Return Error %2d",returnvalue); USART_puts(USART2,out_text); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); USART_puts(USART2," \r "); while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); } if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) blink_green(); } } void delay_nus(uint32_t nus) { uint32_t temp; SysTick->LOAD = nus * 11; SysTick->VAL = 0x00; SysTick->CTRL = 0x01 ; do { temp=SysTick->CTRL; }while(temp&0x01&&!(temp&(1<<16))); SysTick->CTRL = 0x00; SysTick->VAL = 0X00; } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Configuring USART1_Tx as 'alternate function push-pull' */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configuring USART1_Rx as 'input floating' */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); } void Init_Led(void) { // the following statements are for the LEDs /* Initialize GPIO Structure, configure clock on peripheral bus * Enable GPIO Pins for LED which are connected to PC6,7*/ /* Configure the GPIO_LED pin PC6 = Green LED PC7 = Yellow LED * Configure direction and clock speed*/ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); } void Init_TSICPort(void){ GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // Pin Floating //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //Pin Up //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //Pin Down GPIO_Init(GPIOD, &GPIO_InitStructure); } unsigned int read_sw(void){ int dataHalfWord; dataHalfWord = GPIO_ReadInputData(GPIOC); return dataHalfWord; } void blink_yellow() { GPIOC->BSRR = GPIO_Pin_7; //set bit delay_nus(10000); GPIOC->BRR = GPIO_Pin_7; //reset bit //GPIOC->ODR ^= GPIO_Pin_7; // invert pin } void blink_green() { GPIOC->BSRR = GPIO_Pin_6; //set bit delay_nus(10000); GPIOC->BRR = GPIO_Pin_6; //reset bit //GPIOC->ODR ^= GPIO_Pin_6; // invert pin } void toggle_green() { GPIOC->ODR ^= GPIO_Pin_6; // invert pin } void USART_puts(USART_TypeDef* USARTx, volatile char *s){ while(*s){ // wait until data register is empty while( !(USARTx->SR & 0x00000040) ); USART_SendData(USARTx, *s); *s++; } } void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the USART1 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* Enable the USART2 Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //=0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void USART2_IRQHandler(void) { //on overrun clear the OVR condition if (USART_GetFlagStatus(USART2, USART_FLAG_ORE) != RESET) { (void)USART_ReceiveData(USART2); } if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { //Read byte from receive data register bufferRx[RxCounter++] = USART_ReceiveData(USART2); USART_SendData(USART2, bufferRx[RxCounter-1]);//echo if(bufferRx[RxCounter-1]=='\r'){ strcpy(receiveRx, bufferRx); receiveRx[RxCounter-1]='\0'; RxCounter=0;printRx=1; } if(RxCounter > 125) { //Disable the USART1 Receive interrupt USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); } } toggle_green(); } void USART1_SendString(char* data) { while (*data) { USART_SendData(USART1, (uint16_t) *data); while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); data++; } } void USART2_SendString(char* data) { while (*data) { USART_SendData(USART2, (uint16_t) *data); while(USART_GetFlagStatus(USART2, USART_FLAG_TC)==RESET); data++; } } void InitUart2(void) { // Initialize Structs GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; // USART1 RX-Pin initialize PD6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOD, &GPIO_InitStructure); // USART1 TX-Pin initialize PD5 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_Init(GPIOD, &GPIO_InitStructure); /* Remap USART, as USART2 is used as alternate pins on PD5/6*/ GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); // USART initialize USART_ClockStructInit(&USART_ClockInitStructure); USART_ClockInit(USART2, &USART_ClockInitStructure); USART_InitStructure.USART_BaudRate = 19200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART2, &USART_InitStructure); USART_ITConfig (USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); } void RCC_Init(void) { // Route and enable clocks for low and high speed bus RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); } /****************************************************************************** * Description : Reads serial bits from TSIC sensor TSIC 206/208 etc. * Parameters : *temp_value16 : pointer for return raw temp value * channel port IO bit to read from * timeout : timeout in us * Function Returns : 0 = OK or Error, 1 = Timeout, 2 = Parity Error in Byte 1 or Byte 2 ******************************************************************************/ uint8_t readTSicTemp (uint16_t *temp_value16, uint8_t channel, uint32_t timeout) { uint16_t temp_value1 = 0; uint16_t temp_value2 = 0; uint8_t i; uint16_t Temperature; uint8_t parity; uint32_t tim,tstrobe; #define tsic_in GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_1) // wait until falling bit for first strobe pulse high->low // every 100ms a new telegram must arrive, so do not wait longer that 100ms = 100000us // then measure the exact strobe time tim = 0; while ((tsic_in) && (tim < 100000) ){ delay_nus(1); tim++; } if (tim >=timeout) { return 1; } // now we have detected somehow the falling edge. as long as this edge is low we just wait // until it gets high again: this is the strobe and start bit. Typically strobe bit last 60us. // wait until change from low->high then we can start reading bits and so on tim=0; while (!(tsic_in) && (tim < timeout) ){ delay_nus(1); tim++; // measure strobe time which varies with temp and is approx. 60us } if (tim >=timeout) { return 1; } tstrobe = 2*tim; //******************************************************************************* // read first batch of : 8 data bits and 1 parity bit for (i = 0; i < 9; i++) { while (tsic_in); // wait for high->low delay_nus(tstrobe); if (tsic_in) { temp_value1 |= 1 << (8-i); } else while (!( tsic_in )); // wait for low-> high } //******************************************************************************* tim=0; while ((tsic_in) && (tim < 120) ){ tim++; delay_nus(1); } // read second batch of : 8 data bits and parity bit tim=0; while (!(tsic_in) && (tim < timeout) ){ tim++; // measure strobe time which varies with temp and is approx. 60us delay_nus(1); } //******************************************************************************* for (i = 0; i < 9; i++) { while (tsic_in); // wait for high->low delay_nus(2*tim); // strobe time * 2 is the bit time if (tsic_in){ temp_value2 |= 1 << (8-i); // get bit by bit } else while (!( tsic_in )); // wait for low->high } //******************************************************************************* // parity check 1st byte parity = 0; for (i = 0; i < 9; i++) if (temp_value1 & (1 << i)) parity++; if (parity % 2) return 2; // parity check 2nd byte parity = 0; for (i = 0; i < 9; i++) if (temp_value2 & (1 << i)) parity++; if (parity % 2) return 2; // parity NIO temp_value1 >>= 1; temp_value2 >>= 1; Temperature = (temp_value1 << 8) | temp_value2; *temp_value16 = Temperature; return 0; // parity ok value ok so return 0 }
Hier ist das Programm zum Download: