Micromouse book
Categories
Recent Comments
Meta
Popular Posts
- Simple ADC use on the STM32 (4,106)
- STM32 Arm-Cortex bootloader (2,803)
- STM32 USART basics (2,703)
- All Japan Micromouse 2011 – finals (2,011)
- STM32F4 – the first taste of speed (1,690)
- Micromouse Book (1,587)
- Nokia 3410 LCD on the STM32 (1,337)
- CodeSourcery GNU Toolchain for the ARM on a Mac (1,145)
- Bit Banding in the STM32 (1,045)
- ARM STM32 JTAG (973)
Blogroll
-
Upcoming Events
Tag Archives: Blinky
Crossworks Blinky Project 1
With the Rowley Crossworks software set up, it is time to make some code. As usual, it is easiest to start out with a simple LED flasher. Why break with tradition. The target board for this project is the IAR STM32-SK because I happen to have one after winning it in a contest…
This board has a common-or-garden 16×2 LCD, a number of LEDs, four push buttons and various connectors. It is an excellent starting point for STM32 projects. For this example, almost any setup will do so long as you can connect an LED and a pushbutton.
The processor on this board is the STM32F103RB.
In CrossStudio, select File | New Project. If you have the software set up as in the preceding post, You will be given a choice of project types. Select Generic STM32 and ‘An Executable for STMicroelsectronics STM32′ along with a suitable name and folder and press Next.
Now you get to choose your processor and crystal speed along with options for output files and how much you want printf() and scanf() to be able to do.
Again, select Next to see a list of the files that will be automatically linked in to your project.
A final click on Next lets you choose the configurations you want. For now, leave them all ticked.
At this point, you have a blank empty project with nothing but the system startup files included. These are not actually copied into the project folder. Instead, the project properties point to the original files held in the Crossworks installation folder. Consequently, do not edit these files or you will change the startup for every other project that uses them. The project Explorer pane should look like this:
Now we need to add a ‘main’ source file. Select File | New and create a suitable C source code file in the project folder:
And now for the tricky bit. What on earth needs to go in this file? Well, the STM32 can be a pretty tricky beast to configure. The clocks need setting up, the peripherals must be enabled and configured… There are loads of things to do. Rather than go through all the grisly details line by line, simply open the main.c file you just created and drop the following into it.
#include "stm32.h"
#include "targets/STM32F10x.h"
uint32_t state;
void delay(int count) {
volatile int i = 0;
while (i++ < count);
}
// IAR STM32-SK LED1 is on PA4.
// The WAKE-UP button is on PA0
void board_init(void) {
uint32_t temp;
uint32_t mask;
uint32_t mode;
uint32_t pin;
// RCC is the Reset and Clock Control system. Each peripheral needs to have a clock
// signal enabled before it will work. This is achieved by setting a single bit
// in the APB2ENR register of the RCC
RCC_APB2ENR |= RCC_APB2ENR_IOPAEN;
// Once it has a clock, we can configure the port pins as inputs and outputs
// Each GPIO has a Configuration Register. Since there are 16 bits in each register
// and each pin needs four bits to define its state, there are two CRs for each GPIO
// There is a two bit CNF field (the higher two bits):
// Input -
// CNFy = 00 Analog Mode
// CNFy = 01 Floating input (reset state)
// CNFy = 10 Input with pull up/down
// CNFy = 11 Reserved
// Output -
// CNFy = 00 General Purpose Output push-pull
// CNFy = 01 General Purpose Output Open Drain
// CNFy = 10 Alternate Function Output Push-pull
// CNFy = 11 Alternate Function Output Open Drain
//
// There is also a two bit MODE (the lower two bits):
// MODEy = 00 => Input Mode (reset state)
// MODEy = 01 => Output Mode < 10MHz
// MODEy = 10 => Output Mode < 2MHz
// MODEy = 11 => Output Mode < 50MHz
// We want just PA4 to be an open drain output at 10MHz (no reason) so we set
// CNF4 = 01, MODE4 = 01
// so mode = 0b0101 = 0x05
pin = 4;
mode = (uint32_t) 0x05 << (pin * 4);
mask = (uint32_t) 0x0f << (pin * 4);
temp = GPIOA_CRL & ~mask;
GPIOA_CRL = temp | mode;
// And now for the push button WAKE-UP. It is on PA0. We are going to use
// the pin as a standard input rather than use the WAKE-UP alternate function
// on this board, the pin is pulled low by a resistor and the button pulls the input high
// so we want to configure the pin as a floating input.
// from the information above, you will see this is the default state for the pin but, just
// the exercise, we can explicitly configure it. mode = 0b0100 = 0x04
pin = 0;
mode = (uint32_t) 0x04 << (pin * 4);
mask = (uint32_t) 0x0f << (pin * 4);
temp = GPIOA_CRL & ~mask;
GPIOA_CRL = temp | mode;
}
void set_leds(unsigned on) {
if (on)
GPIOA_BSRR = (1 << 4);
else
GPIOA_BRR = (1 << 4);
}
int get_button(void) {
if (GPIOA_IDR & 0x01) {
while ((GPIOA_IDR & 0x01) == 0x01);
return 1;
} else {
return 0;
}
}
void main(void) {
board_init();
state = 0;
while (1) {
switch (state) {
case 0:
set_leds(0);
if (get_button()) {
state = 1;
}
break;
case 1:
set_leds(1);
if (get_button()) {
state = 0;
}
break;
}
}
}
Notice that there are a number of ways of changing the state of an output pin. Each GPIO has two 32-bit data registers (GPIOx_IDR, GPIOx_ODR), a 32-bit set/reset register (GPIOx_BSRR), a 16-bit reset register (GPIOx_BRR) and a 32-bit locking register (GPIOx_LCKR).
GPIOx_ODR holds a copy of the data sent to the Output driver. You can write to it to set the value directly. That does not mean that the pin will assume that state. It may be configured as an input or some device connected to the pin may be holding it in a particular state. Reading the ODR tells you what you set the pin to, not what it is. To read what the pin actually is, read the appropriate GPIOx_IDR. When a pin is configured as an output, a Schmitt trigger is enabled on the input. Thus, the IDR will show the physical state of the pin. When a pin is configured as an input with pullup/pulldown, the value in the ODR is used to determine whether that pin is pulled up or down. these pullups/pulldowns are weak. A pin configured as an analogue input always returns a '0'.
It is common to want to change the state of particular pins on a GPIO without disturbing the value on the other pins. This can be achieved by reading the ODR, masking off the relevant pins, setting the values and writing back. Yes, that is tedious. And it is not atomic. That is, the value on the other pins may be changed by, for example, an interrupt while you are half way through the process and you may write back the wrong value. Instead, the STM32 lets you set or reset a pin with a single write. This is achieved by writing a '1' to the appropriate bit in the GPIOx_BSRR (to set a pin) or GPIOx_BRR (to clear it). If you want to set all the pins in one go then, by all means, just write to the GPIOx_ODR.
The locking register is a 'no turning back' option. Writing a '1' to a bit in the GPIOx_LCKR will ensure that the value on the corresponding pin cannot change until there is a processor reset.
There is often nothing more annoying that trying someone else's example that won't work on your kit but this is pretty straightforward I think. It does rely on you having the same toolchain as that will dictate the location of the relevant include files and the values of the constants defined therein.
If that is so, this should work. The comments should make it relatively easy to change the port pins for the LED and the button. If it woks, the button should toggle the state of the LED. There is no real attempt to debounce the switch. Maybe next time.
Add to Google