Das Olimexino-STM32 Demo Board ist ein sehr preiswertes STM32 Demo-Board für kleine Test- und Bastelanwendungen, aber auch sehr gut um zu lernen wie man denn die STM32 CPUs programmiert. Mit einem Preis von unter 20 Euro kann man dies sicher nicht selbst fertigen.
Hier ist das Board beschrieben. Dort und in vielen Internet-Shops kann man dies sehr preiswert bestellen.
https://www.olimex.com/Products/Duino/STM32/OLIMEXINO-STM32/
Das Bild oben zeigt das Board mit angeschlossenem Programmierstecker, ein RS232/USB-Debug-Board sowie dem angeschlossenen CAN-Bus im unteren Bildbereich. Die Stromversorgung kann über ein einfaches Steckernetzteil von z.B. 12V erfolgen
Ich habe dieses Board als preiswertes Testboard für den CAN-Bus im allgemeinen, aber auch für den Test und als möglicher Ersatz meiner CAN-Jalousie-Controller verwendet.
Wie programmiert man das Board ?
PS: Damit die Timer bzw. delay Funktion richtig funktioniert, muss man darauf achten, dass die Quarzfrequenz richtig eingestellt ist.
Die Frequenz des externen Quarzes im header file stm32f10x.h an folgender Stelle ändern:
#if !defined HSE_VALUE
#ifdef STM32F10X_CL
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#else
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* STM32F10X_CL */
#endif /* HSE_VALUE */
Jetzt folgt nur ein Beispiel wie man den CAN-Bus auf dem Board programmmiert.
/* * Wolfram Koerver 2013 * Demoprogram to test the CAN bus with the OLIMEXINO STM32 board from Olimex. * April 2013 * This program just sends out a few CAN bus telegrams at a CAN bus baud rate of 125kbit/s * The sample program is very simple and not optmized in any way. */ #include "stm32f10x_gpio.h" #include "stm32f10x_can.h" #include "stm32f10x_rcc.h" void GPIO_Blink(void) { int i; /* Initialize LEDs mounted on Olimexino board */ GPIO_InitTypeDef GPIO_InitStructure; /* Initialize LED which is connected to PC6,9 and enable the clocks*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* Configure the GPIO_LED pin */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Toggle LEDs which is connected to PC6*/ GPIOA->ODR ^= GPIO_Pin_1; /* delay */ for(i=0;i<0x500000;i++); /* Toggle LED which is connected to PC9*/ GPIOA->ODR ^= GPIO_Pin_1; /* delay */ for(i=0;i<0x500000;i++); } void CAN_init(){ // define structures GPIO_InitTypeDef GPIO_InitStructure; CAN_InitTypeDef CAN_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // CAN transceiver is connected to Pin 8 and Pin 9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinRemapConfig(GPIO_Remap1_CAN1 , ENABLE); // **** set the CAN baud rate to 125kHz CAN_InitStructure.CAN_Prescaler = 16; CAN_InitStructure.CAN_SJW = CAN_SJW_2tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_5tq; // **** CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = DISABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = ENABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_Init(CAN1, &CAN_InitStructure); } int main(void) { SystemInit(); CAN_init(); CanTxMsg canMessage; // Define the CAN telegram //canMessage.ExtId = 0; canMessage.StdId = 0x22; canMessage.RTR = CAN_RTR_DATA; canMessage.IDE = CAN_ID_STD; canMessage.DLC = 8; canMessage.Data[0] = 0; canMessage.Data[1] = 10; canMessage.Data[2] = 20; canMessage.Data[3] = 30; canMessage.Data[4] = 40; CAN_Transmit(CAN1, &canMessage); GPIO_Blink(); canMessage.Data[0] = 0; canMessage.Data[1] = 11; canMessage.Data[2] = 21; canMessage.Data[3] = 31; canMessage.Data[4] = 41; CAN_Transmit(CAN1, &canMessage); GPIO_Blink(); canMessage.Data[0] = 0; canMessage.Data[1] = 12; canMessage.Data[2] = 22; canMessage.Data[3] = 32; canMessage.Data[4] = 42; CAN_Transmit(CAN1, &canMessage); GPIO_Blink(); }
Dies ist nur ein Minibeispiel um die CAN-Schnittstelle zu konfigurieren und 3 verschiedene CAN-Telegramme auszusenden. Dies auch nur um die grundsätzliche Funktionsweise des CAN Bus mit dem Beagleboard unter LINUX zu testen.
Und so sieht das (TTL)-CAN-Telegram (das letzte) auf dem Logicanalyser aus
SPI1 Schnittstelle
Um einfache Ausgaben auch auf einem kleinen Display auszugeben kann man die SPI-Schnittstelle verwenden. Diese liegt beim Olimexino hier:
SPI1 Pins sind:
SCK : PA5
MISO : PA6
MOSI : PA7
NSS : PA4
Wie das geht steht hier.
Timer und Interrupts
Hier folgt jetzt ein Beispielprogramm wie man die Timer des STM32 programmiert.
#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> #include "stm32f10x_tim.h" /* * Program to test the TIM2 timer and interrupt function for STM32F103RB * tested with OLIMEXINO STM32 Board * Wolfram Koerver - 2013 * * Program starts blinking with approx. 5/sec with the yellow LED on port PA1 * Interrupt is generated every 1000us to flip a signal at PB6 (D5 output) */ void init_LED(void); void blink_yellow(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 IO init_DO(); // init digital output port for digital output signal init_timer(); // configure the Timer Interrupt while(1) { blink_yellow(); // let the main program do something delay_ms(200); } } 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_do(); //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 * Configure direction and clock speed */ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void init_LED(void) { /* Initialize GPIO Structure, configure clock on peripheral bus * Enable GPIO Pin for yellow LED which Configure direction and clock speed */ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); } void blink_yellow() { GPIOA->BRR = GPIO_Pin_1; //reset bit GPIOA->BSRR = GPIO_Pin_1; //set bit delay_ms(1); GPIOA->BRR = GPIO_Pin_1; //reset bit } void toggle_yellow() { GPIOA->ODR ^= GPIO_Pin_7; // invert pin } void toggle_do() { GPIOB->ODR ^= GPIO_Pin_6; // invert pin } void toggle_dot() { GPIOB->BSRR = GPIO_Pin_6; //set bit GPIOB->BRR = GPIO_Pin_6; //reset bit }
Hier jetzt noch der Downloadlink.