Avoimen STM32-ympäristön asennus Ubuntuun

[size=150]Avoimen STM32 -kehitysympäristön asennus Ubuntuun[/size]

Tämän tekstin tarkoituksena esitellä STM32VL Discoveryn käyttöönotto Ubuntussa käyttäen ilmaisia ja vapaita työkaluja ja kirjastoja. Discovery on varsin halpa ja tehokas kehityslauta, joka sisältää ARM Cortex-M3 -pohjaisen, 24 MHz prosessorin, jossa on 128 KB flashia ja 8 KB RAM-muistia. Protoilukäyttöön lauta sopii erinomaisesti kaikkien prossun pinnien piikkirimalle tuonnin ansiosta. Discovery sisältää ohjelmointipiirin ja virransyötön (5V USB:stä tai 5/3.3V ulkoiselta powerilta), joten kehityksen aloittamiseen ei tarvita mitään lisäkomponentteja.

Kääntäjänä tässä ympäristössä käytellään linaro-gcc:tä, joka asennellaan summon-arm-toolchainin yhteydessä. Lisäksi asennetaan stlink, jotta saadaan softat sisälle Discoveryyn ja viimeisenä muttei vähäisimpänä, lisätään settiin libopencm3, joka sisältää varsin kattavat kirjastot useille ARM Cortex-M3 -prossuille. Jonkinaliasta tukea löytyy myös Cortex-M4 -prossuille (lähinnä STM32F4).

Libopencm3 ei ole vielä missään määrin valmis, joten ominaisuuksia, varsinkin apufunktioita ja esimerkkejä puuttuu. Koodia lukemalla hommat kuitenkin selviää ja itse olen ainakin todennut kirjaston varsin päteväksi (suhteellisen pienen) testiprojektini perusteella.

Kaikkien käytettävien ohjelmien uusimmat versiot löytyy suoraan git-versionhallinnasta, joten sieltä asentaminen on helpoin vaihtoehto ja samalla varmistutaan ohjelmien tuoreudesta. Varsinkaan kirjaston kohdalla ei kannata unohtaa päivityksiä myöhemminkään, lisäominaisuuksia varmasti tulee ajan myötä.

Vaikka opas on tehty Discovery silmällä pitäen, sen pitäisi soveltua myös muille kehitysalustoille / prosessoreille.

Varsinaista asennusta en pahemmin ole kommentoinut, koska ainakin itselläni se sujui ongelmitta ja varsin suoraviivaisesti. Jos kysymyksiä tulee, pyrin niihin parhaani mukaan vastaamaan.

[size=150]Esitoimet[/size]

Ensimmäisenä aloitetaan asentamalla tarvittavia työkaluja:

sudo apt-get install flex bison libgmp3-dev libmpfr-dev libncurses5-dev libmpc-dev autoconf texinfo build-essential libftdi-dev libusb-1.0-0-dev pkg-config git

Luodaan kotihakemiston alle tmp-kansio, jossa työskennellä

mkdir ~/tmp

[size=150]summon-arm-toolchainin asennus[/size]

Asennetaan summon-arm-toolchain hakemiston /usr/local -alle. Tähän tarvitaan joko root- tai sudo -oikeudet, joten käsketään asennusskriptin käyttää sudo:ta. Tässä kestää hetki, mutta muista välillä vilkaista onko asennus pysähtynyt kyselemään salasanaa sudo:lle.

cd ~/tmp git clone https://github.com/esden/summon-arm-toolchain.git cd summon-arm-toolchain ./summon-arm-toolchain PREFIX=/usr/local SUDO=sudo

[size=150]stlinkin asennus[/size]

cd ~/tmp git clone https://github.com/texane/stlink.git cd stlink ./autogen.sh ./configure make sudo make install
Asennetaan stlinkin tarjoamat usb-sääntötiedostot, jotta Discoveryn kytkeminen koneeseen toimii niinkuin pitääkin

sudo cp 49-stlinkv1.rules 49-stlinkv2.rules /etc/udev/rules.d sudo udevadm control --reload-rules

[size=150]libopencm3:n asennus[/size]

cd ~/tmp git clone git://libopencm3.git.sourceforge.net/gitroot/libopencm3/libopencm3 cd libopencm3
Käännetään kaikki kirjastot ja esimerkit ‘make’ -komennolla, samalla varmistutaan, että kaikki toimii kuten pitääkin.

make

Kirjastojen asennus järjestelmän laajuiseksi on omasta mielestäni hieman turhaa, itse ainakin jätin seuraavan vaiheen pois…

sudo make install

Tämän jälkeen kytke Discovery tietokoneeseesi. Jos se oli jo kytkettynä, irroita, odota hetki ja kytke uudelleen.

[size=150]Testaus[/size]

Testataan asennuksen toimivuus lataamalla Discoverylle esimerkki ‘button’. Jos kaikki menee oikein, pitäisi Discoveryyn mennä softan, joka vilkuttaa vihreää lediä ja hidastuu, kun painetaan sinistä nappia…

cd ~/tmp/libopencm3/examples/stm32/f1/stm32vl-discovery/button make clean && make && st-flash write button.bin 0x8000000
Eli lyhykäisyydessään ensin puhdistetaan vanhat käännöstiedostot, käännetään uudelleen ja tämän jälkeen flashataan ‘button.bin’ Discoveryn flash-muistiin.

[size=150]Dokumentaation “kääntäminen”[/size]

Jos haluaa libopencm3:n (heikkoa, lähes olematonta) dokumentaatiota lukea selaimen kautta, kannattaa tehdä seuraavat toimenpiteet:

sudo apt-get install doxygen cd ~/tmp/libopencm3 doxygen Doxyfile
Tämä luo dokumentaation html-muotoisena ja sitä pääsee näppärästi lukemaan kirjoittelemalla selaimen osoiteriville file:///home/KÄYTTÄJÄTUNNUS/tmp/libopencm3/doxygen/html/index.html

[size=150]Loppusanat[/size]

Tässä lyhykäisyydessään kehitysympäristön pystyttäminen Discoverylle uusimpaan Ubuntuun. Ikävä kyllä oma kokemukseni gdb:stä jne on sen verran heikkoa, etten siitä nyt alkanut mitään vielä kirjoittelemaan, mutta stlinkin kautta senkin pitäisi onnistua. Jos tuntuu, ettei libopencm3:n käyttö oikein luonnistu, examples-hakemistoa selailemalla löytyy jonkin verran esimerkkejä. Ei kannata rajoittua prossukohtaisiin esimerkkeihin, myös muille prosessoreille tehtyjen esimerkkien koodia voi hyödyntää varsin hyvin. Ja jos esimerkkiä ei löydy, niin koodia lukemaan :wink:

Kannattaa myös muistaa päivittely välillä, se onnistuu helposti (jos ei tmp-kansiota poista) menemällä sopivaan kansioon, vaihtamalla ‘git clone ’ -komento ‘git pull’ -komentoon ja seuraamalla ohjeet loppuun.

Kommentteja otetaan vastaan ja niitä saatetaan myös lukea. Oppaan ei ole tarkoitus olla kaiken kattava, mutta hyvään alkuun tällä pääsee. Toivottavasti tämä kuitenkin herättelee keskustelua aiheesta ja kenties hyviä koodivinkkejäkin näkyisi aiheen tiimoilta.

[size=150]Lähteet[/size]

github.com/esden/summon-arm-toolchain/
github.com/texane/stlink/
libopencm3.org/wiki/Main_Page

//edit 03.09.2012: Yksi huomaamani bugi korjailtu.

Tämä kannattaa tarkistaa, mikäli mietiskelee libopencm3:n tilannetta:

libopencm3.org/wiki/Status

Tässä vielä yksi omista testikoodeistani. Koodin laatua on turha kommentoida, se on vain pikainen testi :wink:

Käytössä seuraavanlaista kikkaretta:

  • USART1, jonka kautta luetaan ja lähetetään dataa.
  • timer2, asetettuna pyörähtämään sekunnin välein ja tällöin lähetetään timerin arvo (uptime) sarjaporttiin. Lisäksi kytketään ledi GPIOC9-portissa päälle/pois.

Timer voidaan resetoida lähettämällä sarjaporttiin komento “r!”. Lisäksi voidaan vilkutella sinistä lediä (GPIOC8) komennolla “0!”.

Sarjaporttiin kirjoitettu data kirjoitetaan muistiin bufferiin ja palautetaan, kun lähetetään “\r” (ts. enter). Huutomerkkiä tällä koodilla ei pystytä kirjoittamaan, koska se on varattu “komentomerkiksi”.

[code]#include <stdio.h>
#include <string.h>
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/stm32/f1/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/nvic.h>
#include <libopencm3/stm32/timer.h>

#define bufsize 1024
typedef struct usart_buffer {
unsigned int i;
char buf[bufsize];
} usart_buffer;

usart_buffer out;

unsigned int timer_count = 0;

void gpio_setup(void)
{
/* Enable GPIOC clock. */
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPCEN);

/* Set GPIO9 (in GPIO port C) to 'output push-pull'. [LED] */
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
            GPIO_CNF_OUTPUT_PUSHPULL, GPIO8 | GPIO9);
gpio_clear(GPIOC, GPIO8 | GPIO9);

}

void timer_setup(void) {
// Enable timer interrupt
nvic_enable_irq(NVIC_TIM2_IRQ);
nvic_set_priority(NVIC_TIM2_IRQ, 3);
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN);
// Set timer start value
TIM_CNT(TIM2) = 1;
// Timer prescaler
// 24 MHz / 600 -> 40000 -> prescaler 600, end value 40000 for 1 second delay
TIM_PSC(TIM2) = 600;
// Timer end value
TIM_ARR(TIM2) = 40000;
// Interrupt enable
TIM_DIER(TIM2) |= TIM_DIER_UIE;
// Start timer
TIM_CR1(TIM2) |= TIM_CR1_CEN;
}

void usart_setup(void) {
// Enable USART1 interrupts
nvic_enable_irq(NVIC_USART1_IRQ);
nvic_set_priority(NVIC_USART1_IRQ, 1);
/* Enable clocks for GPIO port A (for GPIO_USART1_TX) and USART1. */
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN);
rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_USART1EN);

/* Setup GPIO pin GPIO_USART1_TX/GPIO9 on GPIO port A for transmit. */
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
	      GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);

/* Setup GPIO pin GPIO_USART1_TX/GPIO10 on GPIO port A for recieve. */
gpio_set_mode(GPIOA, GPIO_MODE_INPUT,
	      GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX);

/* Setup UART parameters. */
// usart_set_baudrate(USART1, 38400);
/* TODO usart_set_baudrate() doesn't support 24MHz clock (yet). */
/* This is the equivalent: */
USART_BRR(USART1) = (u16)((24000000 << 4) / (38400 * 16));

usart_set_databits(USART1, 8);
usart_set_stopbits(USART1, USART_STOPBITS_1);
usart_set_mode(USART1, USART_MODE_TX_RX);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);

// Enable USART1 RX interrupt
USART_CR1(USART1) |= USART_CR1_RXNEIE;

// Enable USART1
usart_enable(USART1);

}

void send_to_usart1(char *msg, unsigned int len) {
volatile unsigned int i = 0;
for (i = 0; i < len; i++) {
usart_send_blocking(USART1, msg[i]);
}
usart_send_blocking(USART1, ‘\r’);
usart_send_blocking(USART1, ‘\n’);
}

void usart1_isr(void) {
volatile char c;
c = usart_recv_blocking(USART1);
if (c == ‘\r’) {
if (out.i > 0)
send_to_usart1(out.buf, out.i);
out.i = 0;
}
else if (c == ‘!’) {
volatile char d;
d = out.buf[out.i-1];
switch (d) {
case ‘0’:
gpio_toggle(GPIOC, GPIO8);
break;
case ‘r’:
TIM_CNT(TIM2) = 1;
TIM_CR1(TIM2) |= TIM_CR1_CEN;
timer_count = 1;
send_to_usart1(“timer reset”, 11);
break;
default:
break;
}
out.i = 0;
}
else {
out.buf[out.i] = c;
out.i++;
}
}

void tim2_isr(void) {
volatile unsigned int i, hour, min, sec;
char msg[bufsize];
gpio_toggle(GPIOC, GPIO9);
hour = timer_count / 60 / 60;
min = timer_count / 60 - hour60;
sec = timer_count - min
60 - hour6060;
if (hour > 0)
i = sprintf(msg, “%d hours, %d minutes, %d seconds”, hour, min, sec);
else if (min > 0)
i = sprintf(msg, “%d minutes, %d seconds”, min, sec);
else
i = sprintf(msg, “%d seconds”, sec);
send_to_usart1(msg, i);
timer_count++;
// Clear interrupt flag
TIM_SR(TIM2) &= ~TIM_SR_UIF;
}

int main(void) {
rcc_clock_setup_in_hse_8mhz_out_24mhz();
gpio_setup();
usart_setup();
send_to_usart1(“started”, 7);
timer_setup();

while (1) {
}

return 0;

}[/code]