📜 ⬆️ ⬇️

First Steps with Stellaris LM4F120 launchpad evaluation board

The board of Stellaris LM4F120, which I finally decided to figure out, was dumped in my desk. We will write a program that turns on / off the LEDs installed on the board in response to pressing the buttons on the board.

I’ll say right away that it’s a bit tough with electronics, so the article will express my opinion on how it works, which may not coincide with reality. My terminology is very bad, so if you know the correct term, please correct it.

Training

First, you need to install the necessary software for development:


With compilers and IDE, Texas Instruments provides a choice : their own Code Composer Studio based on Eclipse (can be installed both separately and as a plug-in to an already installed Eclipse, if any), IAR Embedded Workbench, Keil and Mentor Graphics Sourcery CodeBench:

')
The free version of CCS is fully functional and I will use it for her. By the above link, you can download the “CD”, hiding behind the part number of EK-LM4F120XL-CCS, which will have everything you need for development, but the contents of the archive are somewhat out of date, and do not seem to be updated. But you can download all separately:

Code Composer Studio , StellarisWare is hidden behind part number SW-EK-LM4F120XL here , Stellaris ICDI drivers , LMFlash programmer , Evaluation board User's Manual , Datasheet . After installing StellarisWare in the directory where it was installed, you will be able to find the docs subdirectory with the documentation. The SW-DRL-UG-9453.pdf file is of primary interest (the numbers may vary depending on the SW version) - the documentation for the functions provided by the SDK (also, if you look at SW-EK-LM4F120XL-UG-9453.pdf, you can find high-level functions to work directly with the LEDs and buttons, but this is not the way of the real Jedi: we want to understand what is happening inside?)

I must say that some renaming took place in TI, and the LM4F120H5QR microcontroller used in the board began to be called TM4C1233H6PM, respectively, at the moment you should use the second name to search for information.

On the Texas Instruments website, you can find video lectures about the board and printed materials for them.

We connect the board to the computer:

We connect the USB cable to the debugger port, make sure that the power switch (next to the USB port) is in the “debug” position. If everything is done correctly, except for the green power LED, the LED next to the “reset” button should light up, and after 5 seconds, start flashing with different colors (if, of course, you did not have time to flash something different from the factory firmware).

Windows 8.1 hint. This default operating system does not allow loading unsigned drivers, which makes it impossible to install ICDI drivers. This is treated as follows: press Win + I → Power, hold Shift and click on Restart, wait, a menu with repair options will appear, select Troubleshoot → Advanced options → Startup settings → Restart. After the reboot, a menu will appear in which you must select “Disable driver signature enforcement”.

The very first program


So, we have installed everything you need. It's time to write. Create a project in CCS: File → New → CCS Project. Configure similar to the picture:

We will get a project in which there will be two source files at once: an empty main.c and lm4f120h5qr_startup_ccs.c . The second file contains the boilerplate code for initializing the interrupt table and the stub function for interrupt handling.

Immediately configure the path to search for header files and libraries, go to the menu Project → Properties, then:


Of course, replace the paths with your own.

Add #include to main.c and check that everything compiles:
 #include <inc/hw_types.h> #include <inc/hw_gpio.h> #include <inc/hw_memmap.h> #include <driverlib/sysctl.h> #include <driverlib/gpio.h> int main(void) { return 0; } 


If it does not work, we repair it; if it works, you can begin to do something useful.

Operating frequency


First of all, you need to adjust the frequency at which our microcontroller will work. The maximum frequency with which it can work is 80 MHz, but less is possible. Section 5.2.5 of the data sheet tells us how to adjust the frequency and scares with Figure 5-5:

At the moment, we are interested in the release of "System clock". According to the data sheet, as a source of clock pulses can be:


Two of these sources are external, with respect to the microcontroller (Main OSC and Hibernation OSC), the rest are inside it.

The divider, indicated in the diagram as SYSDIV, can be set to 1x – 64x frequency reduction. If PLL is used as the source, the frequency divider by 2 can be enabled, controlled by the DIV400 bit (enabled by default). For the operation of the PLL, a master pulse generator is required. Its frequency can vary widely, but it must be specified at initialization. Using PLL is the only way to start the microcontroller at the maximum frequency, so we will use it (although for our purposes such a crazy frequency is not needed). For clocking PLL can be used as an external crystal, and internal.

StellarisWare offers the SysCtlClockSet function to configure everything that is related to the System clock in one fell swoop. To do this, she enters a bunch of flags at the entrance:
To begin with, we’ll define whether we use PLL ( SYSCTL_USE_PLL ) or something else ( SYSCTL_USE_OSC ) - the BYPASS flag in the diagram.
If you use PLL, you need to choose who will be the master oscillator: Main OSC ( SYSCTL_OSC_MAIN ) or Precision Internal OSC ( SYSCTL_OSC_INT ). If you use Main OSC, you must specify its frequency (in our case, SYSCTL_XTAL_16MHZ ).
If you do not use PLL, you must specify the source:


And finally, you need to configure the divider. For this there are macros from SYSCTL_SYSDIV_1 to SYSCTL_SYSDIV_64 . If PLL is used, you can additionally use macros SYSCTL_SYSDIV_2_5 (/2.5) - SYSCTL_SYSDIV_63_5 (/63.5). For reasons unknown to me, for calling SysCtlClockSet PLL frequency is considered equal to 200 MHz, i.e. in order to operate at a frequency of 80 MHz, you must specify a divider 2.5.

So, the SysCtlClockSet call might look like this:
  //  PLL,   - Main OSC   16 ,   2.5,   80  SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_SYSDIV_2_5 | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); 

or so:
  //  PLL,   - Precision Internal OSC,   2.5,   80 ,  Main OSC SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_SYSDIV_2_5 | SYSCTL_OSC_INT | SYSCTL_MAIN_OSC_DIS); 

Or as you please.

Blink LEDs


It's time to make a program, the result of which can be seen. The documentation on the board has a LED on circuit:


Those. the LEDs are connected to the feet of 1-3 GPIO port F (if anyone knows why there is a resistor between the base and the emitter of the transistor - please enlighten, and why is its value not indicated?). The GPIO port has eight legs, each of which can be individually configured as an input or output. Writing and reading is carried out with all legs at once by writing or reading the register GPIODATA , mapped into memory (respectively, read / write one byte - the state of all legs). Access to the port can be done in two ways: through Advanced Peripheral Bus (APB) or through Advanced High-performance Bus (AHB). The first method is “old” and slow, the second is modern and fast. Honestly, what difference between them in terms of programming, I do not know (at least the basic addresses for ports differ), I used APB, the base address for port F is 0x40025000.

So, in order to light the LED it is necessary to set a high voltage level on the corresponding foot. Those. write 1 to the appropriate bit. Usually, to change the state of a bit, you need to read the state of the register, set the values ​​of the necessary bits and write the resulting value back. Memory operations are slow, so the developers of the microcontroller provide an opportunity to do with one entry (without prior reading): the register is accessed not by one address, but by a range of 1024 bytes starting at the base address. In bits 2–9 of the address, a mask of bits is placed, the values ​​of which must be updated. Those. if we want to write 1 in bit number 1, we must take the corresponding mask: 0x02, move it to the left by 2 bits and add the resulting value to the base address - we get the address to write down the required port state (0x02 or 0xFF - it will still be taken only the first bit).

The function of setting the necessary bits could look like this:

 void pinWrite(unsigned int base, unsigned char pins, unsigned char value) { *((unsigned char *)base + ((unsigned int)pins << 2)) = value; } 


StellarisWare provides the GPIOPinWrite function with the same signature that does the same.

Only one thing is missing: you need to turn on the port and configure the legs to which the LEDs are connected as exits. For those who want to do without the SDK, the process is described in section 10.3 of the microcontroller's data sheet. With StellarisWare, this is done by calling SysCtlPeripheralEnable and GPIOPinTypeGPIOInput / GPIOPinTypeGPIOOutput .

 #include <inc/hw_types.h> #include <inc/hw_gpio.h> #include <inc/hw_memmap.h> #include <driverlib/sysctl.h> #include <driverlib/gpio.h> const unsigned int LED_RED = 0x02; const unsigned int LED_GREEN = 0x08; const unsigned int LED_BLUE = 0x04; const unsigned int LEDS_ALL = 0x02 | 0x08 | 0x04; int main(void) { SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_SYSDIV_2_5 | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); SysCtlPeripheralEnable(SYSCTL_PERIPH2_GPIOF); SysCtlDelay(2); GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, LEDS_ALL); const unsigned long int delay = 80000000 / 3 / 2; // 80 , 3    , 1/2    while (1) { GPIOPinWrite(GPIO_PORTF_BASE, LEDS_ALL, LEDS_ALL); SysCtlDelay(delay); GPIOPinWrite(GPIO_PORTF_BASE, LEDS_ALL, 0); SysCtlDelay(delay); } return 0; } 


The SysCtlDelay function SysCtlDelay n iterations of the empty loop, allowing you to make delays of the desired length. Each iteration lasts 3 processor cycles. Compile, run (F11 in CCS, then it will be necessary to press F8, because the default at the beginning of the program is an implicit breakpoint). The LED on the board should start blinking white with a period of one second. If instead of LEDS_ALL third argument of the first GPIOPinWrite call GPIOPinWrite passed, for example, LED_GREEN , then the blink will be green.

Buttons!



Two buttons are connected to feet 0 and 4 of the GPIO port F according to this scheme:

From the diagram it can be seen that pressing the button connects the foot to the ground, and in the unpressed state the foot is not connected anywhere. This is bad, if you try to read anything from an unconnected foot, it can be read. We need to connect the supply voltage when the button is not pressed. This will help us pull up (pull-up), which practically connects the leg to the power through a resistor, so that when the button is not pressed, the supply voltage through the tightening will come, and when pressed, the current from it will go to the ground, those. the released button will be read as a unit in the port, pressed - as zero.

So, we adjust legs 0 and 4 on an input, we include pull-up:
  GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4); GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); 


The GPIOPadConfigSet function allows GPIOPadConfigSet to set the current that the foot (2, 4 or 8 mA) and its mode can issue. It is worth noting that the microcontroller has protection against changing the settings of some legs of some GPIO ports (those that can be used for the JTAG / SWD port (bits 0–3 of the C port) or for the nonmaskable interrupt input — NMI (7 bits of the D port and 0 bits port F)): you can change the pull-up / pull-down settings for these legs only if you write 1 in the GPIOCR register. GPIOCR can only write the GPIOLOCK register GPIOLOCK is a special “magic number” in the GPIOLOCK register.

Putting it all together:
 #include <inc/hw_types.h> #include <inc/hw_gpio.h> #include <inc/hw_memmap.h> #include <driverlib/sysctl.h> #include <driverlib/gpio.h> const unsigned int LEDS_ALL = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; const unsigned int SW1 = GPIO_PIN_0; const unsigned int SW2 = GPIO_PIN_4; void crSet(unsigned int base, unsigned char value); int main(void) { int led = 2; SysCtlClockSet(SYSCTL_USE_PLL | SYSCTL_SYSDIV_2_5 | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN); SysCtlPeripheralEnable(SYSCTL_PERIPH2_GPIOF); SysCtlDelay(2); GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, LEDS_ALL); GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, SW1 | SW2); HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY_DD; crSet(GPIO_PORTF_BASE, 1); GPIOPadConfigSet(GPIO_PORTF_BASE, SW1 | SW2, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU); crSet(GPIO_PORTF_BASE, 0); HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0; while (1) { unsigned int state = ~GPIOPinRead(GPIO_PORTF_BASE, SW1 | SW2); led = ((state & SW1) << 1) | ((state & SW2) >> 1); GPIOPinWrite(GPIO_PORTF_BASE, LEDS_ALL, led); } return 0; } void crSet(unsigned int base, unsigned char value) { unsigned long v = (HWREG(GPIO_PORTF_BASE + GPIO_O_CR) & 0xFFFFFF00) | value; HWREG(base + GPIO_O_CR) = v; } 


This wonderful program and a fee for 12 bucks, finally, allows you to do the same thing as a simple shemka of less than a dozen parts, worth 10 rubles, congratulations!

Source: https://habr.com/ru/post/206864/


All Articles