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 &quot;stm32f10x_gpio.h&quot;
#include &quot;stm32f10x_can.h&quot;
#include &quot;stm32f10x_rcc.h&quot;
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, &amp;GPIO_InitStructure);
	/* Toggle LEDs which is connected to PC6*/
	GPIOA-&gt;ODR ^= GPIO_Pin_1;
	/* delay */
	for(i=0;i&lt;0x500000;i++);
	/* Toggle LED which is connected to PC9*/
	GPIOA-&gt;ODR ^= GPIO_Pin_1;
	/* delay */
	for(i=0;i&lt;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, &amp;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, &amp;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, &amp;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, &amp;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, &amp;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, &amp;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.