To be edited:
Preparation
Buy the “STM32L-Discovery” (S$16.28, before GST, 2012-Jun-15);
an
A-to-miniB USB cable;
one piece of wire-wrapping board, pins, wires, and the wire-wrapping tool;
one piece of 74HC164, one piece of 7-segment display, 8 resistors(330).
Download UM1079 User
Manual STM32L-Discovery
RM0038
Reference Manual …STM32L152xx… MCUs
UM1451
Getting started … toolchains
KEIL_mdk 4.53 (uV4 IDE)
STM32L152xx
Data Sheet
STM32L_Discovery Firmware
Cortex-M3
Technical Reference Manual (r2p0)
Install
the uV4 IDE in an easily accessible folder
the STLink driver from one of the IDE sub-folders
Read
the User Manual
the
Getting started … user manual
the
Reference Manual.
GPIO output
Project Creation
Connect the STM32L-Discovery board
to the computer.
Create a suitable folder, (e.g. WorkSpace).
Launch the uV4 IDE.
Click
Project | New uVision Project… | T0830P | STM32L152RB
| Yes
Highlight Target1 in Project window
Click
Project | Options… | Debug | Use ST-Link (Deprecated
Version) | Settings | SWD
| Utilities | ST-Link
(Deprecated Version)
Program 1
Click
File | New… | Edit and save the program below as P11PBO22.c
Add this file to Source Group 1
//
P11PBO22.c Port B Output bit 6 (Blue LED) ON
#include
"stm32l1xx.h"
int
main(void){
RCC->AHBENR |=
RCC_AHBENR_GPIOBEN ;
GPIOB->MODER |= 0x01<<(2*6)
;
// Output(01) bit6
GPIOB->ODR
|= 1<<6
;
// Set bit6
while(1);
}
void
SystemInit(void){
}
(The various editors have different
indentation and formatting styles.)
Rebuild
Start Debug Session (OK)
Run
The blue
LED at PB6 (Port B bit 6) will be turned ON.
Stop
Stop Debug Session
Note:
“Safety remove … dongle” before disconnecting the board.
Discussion
The blue LED is connected to GPIO
Port B bit 6, with the cathode grounded. GPIO ports are getting rather
complicated. The clock input has to be enabled before the port can be used.
Each port pin may function as input, output or an alternate function pin
etc.
Terms such as RCC_AHBENR_GPIOBEN are defined in the stm32l1xx.h file.
The reset condition for GPIOB->MODER is available in the Reference Manual.
The SystemInit function will be used in later programs.
This is about the simplest program you
can write to show visible result on the board.
The program may be simple but to
make it work on the board may not. As the user moves from one version of the
software to another, the driver or the firmware may not be compatible. So
turning on the blue LED is quite a task.
GPIO Configuration
GPIO ports are getting rather
versatile and require some configurations before being used.
These include the output type which
can be push-pull or open-drain, the speed and whether a pull-up or pull-down
resistor can be activated. Program 1 assumes the reset state and selects the
push-pull mode for the output type. Although the reset condition is good enough
for the LED in early applications, it is a good habit to have all conditions
defined, if possible. It is also better to put these configurations in a
function. To avoid changing the function all the times, we will have the
function as complete as possible, with push-pull output and floating input.
Since the configuration is usually done once and has usually no visible output,
it is good to put it in the SystemInit function.
As the PinCfgB function is placed before the SystemInit function, the prototype declaration is not required. The
function may be improved to include other options.
Rename and rewrite the program as
follows:
//
P11PBO27.c LED's with Pin Configuration Function
#include
"stm32l1xx.h"
int
main(){
int i,j ;
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
GPIOB->ODR ^=
1<<7 ;
// Invert bit7
GPIOB->ODR ^=
1<<6 ;
// Invert bit6
for(i=0;i<=200;i++){for(j=0;j<=1000;j++);} // Software delay
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
GPIOB->MODER &= ~(0x03<<(2*PIN))
; // Mode mask
GPIOB->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOB->OTYPER &=
~(1<<PIN) ; // PushPull(0)
GPIOB->OSPEEDR &= ~(0x03<<(2*PIN))
; // Speed mask
GPIOB->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOB->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOB->PUPDR |=
(0x00<<(2*PIN)) ; // No PUPD(00)
GPIOB->ODR &=
~(1<<PIN)
; // Clear
PIN
if(PIN<=7){
GPIOB->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOB->AFR[0] |= FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOB->AFR[1] &= ~(0x0F<<(4*(PIN-8))); // AFR
mask
GPIOB->AFR[1] |= FUNC<<(4*(PIN-8)) ; //
Alt Func
}
}
void
SystemInit(void){
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
}
System Clock
So far the program is running at the
default speed of about 2 MHz using the MSI(multispeed internal) clock. Sooner
or later, you have to select a clock which would be used throughout the project
or possibly for most projects. The selection has to take into consideration the
crystal mounted, the RTC (real-time clock) requirement and the wait states for
the program code fetching. For the STM32L-DISCOVERY, only the 32.768-kHz
crystal is mounted. The PCB provides for an external crystal but this is not
fitted. However, it is possible to get an external 8-MHz clock from the
debugger by closing the SB17 bridge through soldering. In later stages, the PLL
can be enabled to run the CPU at a higher speed with additional wait states.
For now, a clock speed of 8 MHz is actually adequate for many applications.
Besides, at this 8-MHz speed, we do not have to worry about wait states.
Note that the ADC requires the use
of the HSI clock of about 16-MHz. This can be tested first. But at this speed,
either the core voltage has to be increased to 1.8 V or a wait state has to be
introduced.
To avoid the wait state
manipulation, we shall choose a core voltage of 1.8 V, so that the system clock
can be later switched between the 8-MHz HSE or the 16-MHz HSI. This step proved
to be a bit difficult, and the result can only be verified by checking the
PWR->CR register by software.
The following program changes the
core voltage to 1.8 V.
//
P12PWR22.c Power Control Core Voltage = 1.8 V
#include
"stm32l1xx.h"
int
main(){
int i,j ;
while(1); // Stop here for observation
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
GPIOB->ODR ^=
1<<7 ;
// Invert bit7
GPIOB->ODR ^=
1<<6 ;
// Invert bit6
for(i=0;i<=200;i++){for(j=0;j<=1000;j++);} // Software delay
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
// Core Voltage
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
if( (PWR->CR&PWR_CR_VOS)==PWR_CR_VOS_1 ) GPIOB->ODR |= 1<<7 ;
// 10 here
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0'
// PWR->CR &= ~PWR_CR_VOS
; // This is forbidden
// PWR->CR |= PWR_CR_VOS_0
; // Then this does not work
PWR->CR = (PWR->CR&(~PWR_CR_VOS)) | PWR_CR_VOS_0 ;
// 10->00->01
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0' again
if( (PWR->CR&PWR_CR_VOS)==PWR_CR_VOS_0 ) GPIOB->ODR |= 1<<6 ;
// 01 here
}
Both LED’s should be lighted.
HSI
With a core voltage of 1.8 V, we can
switch over to HSI at 16-MHz without introducing any wait states.
The earlier program to blink the
LED’s is edited to run from the HSI.
The LED’s would change more rapidly
compared to the previous program.
The accuracy of the 16-MHz clock can
be measured in later programs on timer interrupts.
//
P13HSI22.c HSI clock with Core Voltage = 1.8 V
#include
"stm32l1xx.h"
int
main(){
int i,j ;
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
GPIOB->ODR ^=
1<<7 ;
// Invert bit7
GPIOB->ODR ^=
1<<6 ;
// Invert bit6
for(i=0;i<=200;i++){for(j=0;j<=1000;j++);} // Software delay
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
// Core Voltage
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0'
// PWR->CR &= ~PWR_CR_VOS
; // This is forbidden
// PWR->CR |= PWR_CR_VOS_0
; // Then this does not work
PWR->CR = (PWR->CR&(~PWR_CR_VOS)) | PWR_CR_VOS_0 ;
// 10->00->01
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0' again
// HSI
RCC->CR |= RCC_CR_HSION ;
while( (RCC->CR&RCC_CR_HSIRDY)==0 );
// Wait till HSI Ready
RCC->CFGR &= ~RCC_CFGR_SW
;
// Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSI
;
// Switch to HSI
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait till HSI
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
}
Varying LED
With the system clock settled, we
can edit the more interesting program as follows:
//
P13HSI28.c LED Vary (HSI)
#include
"stm32l1xx.h"
int
main(){
int i,k ;
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR |= 1<<7
; // Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR &=
~(1<<7) ; // Clear PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
}
HSE
Let us change over to the 8-MHz HSE.
Solder the SB17 bridge to connect
the 8-MHz MCO output from the debugger to the OSC_IN of the STM32L152.
Solder the SB18 bridge to bring this
clock to the PH0 pin on the P1 connector.
This can be verified with a
voltmeter or an LED at the PH0 pin.
The last two programs are edited to
run from the HSE at 8-MHz.
The accuracy of the 8-MHz clock can
be verified in later programs on timer interrupts.
This is the second program.
//
P14HSE28.c LED Vary (HSE)
#include
"stm32l1xx.h"
int
main(){
int i,k ;
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR |= 1<<7
; // Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR &=
~(1<<7) ; // Clear PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
// Core Voltage
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0'
// PWR->CR &= ~PWR_CR_VOS
; // This is forbidden
// PWR->CR |= PWR_CR_VOS_0
; // Then this does not work
PWR->CR = (PWR->CR&(~PWR_CR_VOS)) | PWR_CR_VOS_0 ;
// 10->00->01
while( (PWR->CSR&PWR_CSR_VOSF)!=0 ) ; // Wait till '0' again
// HSI
//RCC->CR |= RCC_CR_HSION ;
//while( (RCC->CR&RCC_CR_HSIRDY)==0 );
// Wait till HSI Ready
//RCC->CFGR &= ~RCC_CFGR_SW
;
// Clock Switch Mask
//RCC->CFGR |= RCC_CFGR_SW_HSI
;
// Switch to HSI
//while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI
); // Wait till HSI
// HSE
RCC->CR |= RCC_CR_HSEBYP
; // Use external clock (MCO)
RCC->CR |= RCC_CR_HSEON ;
while( (RCC->CR&RCC_CR_HSERDY)==0 );
// Wait till HSE Ready
RCC->CFGR &= ~RCC_CFGR_SW
; //
Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSE
; // Switch to HSE
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSE ); // Wait till HSE
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
}
The speed of the changing LED’s
indicates the clock had changed.
GPIO Input
For user control, let us add the
push button at PA0, which is pulled down externally.
The previous configuration function
can just be modified for port A.
In this and later programs, the main
function will not continue until the button is pressed and then released.
//
P15PAI22.c Port A bit 0 Intput Push Button
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
int
main(){
int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=5;k<=1995;k++){
GPIOB->ODR |= 1<<7
; // Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR &=
~(1<<7) ; // Clear PB7
for(i=0;i<=(2000-k);i++);
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
// Input Push Button
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0); // PA0 Push Button
}
That ends the simple GPIO input and
outputs.
IAR Embedded Workbench
The program development is now
attempted using the IAR software.
Download IAR Embedded Workbench
KickStart Edition 6.40.1
Install
the Embedded Workbench (IDE) in an easily accessible folder
(The STLink driver installed for uV4 can still be used.)
Project Creation and GPIO output
Connect the STM32L-Discovery board
to the computer.
Create a suitable folder, (e.g. WorkSpace).
Search, and make a copy of the
following files in this folder:
stm32l1xx.h
system_stm32l1xx.h
startup_stm32l1xx_md.s
Launch the IAR Embedded Workbench
IDE.
Follow the “Getting Started …” user
manual to create a new project, and add $TOOLKIT_DIR$\..\arm\CMSIS\Include to the include directories of the preprocessor of the C
Compiler.
Add startup_stm32l1xx_md.s to the project.
Program 1
Click
File | New | File, edit and save the program below as P11PBO22.c
Add this file to the project.
// P11PBO22.c Port B Output bit 6 (Blue
LED) ON
#include "stm32l1xx.h"
int main(void){
RCC->AHBENR |=
RCC_AHBENR_GPIOBEN ;
GPIOB->MODER
|= 0x01<<(2*6)
; // Output(01)
bit6
GPIOB->ODR |=
1<<6
;
// Set bit6
return 0;
}
void SystemInit(void){
}
Make
Download and Debug
Go
The blue
LED at PB6 (Port B bit 6) will be turned ON.
Stop Debugging
Note:
“Safety remove … dongle” before disconnecting the board.
Discussion
The procedure is different for
different IDEs/compilers. If the procedure described in the “Getting Started …”
manual is not followed, the downloading may not be done properly. The header
files are not automatically located by this compiler. In fact, three header
files for the core are contained in the $TOOLKIT_DIR$\..\arm\CMSIS\Include directory. The above program would still work even if the startup_stm32l1xx_md.s file is not included. But subsequent programs using the SystemInit function will not work. So turning on the blue LED is
useful to check that the software and the hardware work together. The other
programs can be similarly tested and this leaves us with about one possible
worry, the ‘interrupt’, which may be again different for different IDE’s.
SPI
Hardware Extension
Although there is an LCD on the
board, it is quite difficult to use. So we shall leave it until much later. The
SPI is usually available on most microcontrollers and should be studied first.
The terminals for this microcontroller’s SPI can be mapped to different pins.
It is necessary to read the user manual for the Discovery and the Data Sheet to
choose the appropriate pins for SPI. In the subsequent programs, SPI1 has been
used with SCK mapped at PA5, MOSI at PA12 and MISO at PA11. You may note that
these pins are free-to-use on the Discovery board and their use will not
interfere with the on-board LCD.
SPI1
SCK PA5 Free
I/O P1
20
SPI1 MISO
PA11 Free
I/O P2
20
SPI1 MOSI
PA12 Free
I/O P2
19
Connect the SPI pins (SCK and MOSI)
of the microcontroller to a piece of 74HC164. The 8 outputs of the 74HC164 are
then connected, with resistors, to a 7-segment display.
The connection can be done by
wire-wrapping.
The SPI pins have to be configured
with the alternate function code of 0x05.
The following program display the
digit ‘3’, wait for the user button to be activated, and then display the three
horizontal bars.
//
P16SPI22.c SPI1 Initialisation
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
int
main(){
int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
SPI1->DR = 0x49
;
// Test with another
code
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=5;k<=1995;k++){
GPIOB->ODR |= 1<<7
; // Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR &=
~(1<<7) ; // Clear PB7
for(i=0;i<=(2000-k);i++);
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
// LEDs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0); // PB7 LED Green
PinCfgB(6,1,0); // PB6 LED Blue
// Input Push Button
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0); // PA0 Push Button
// SPI1
RCC->AHBENR |= RCC_AHBENR_GPIOAEN
;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN ;
PinCfgA(5,2,5); //
PIN=5, AF SCK
PinCfgA(12,2,5); // PIN=12, AF MOSI
PinCfgA(11,2,5); // PIN=11, AF
MISO
SPI1->CR1 = SPI_CR1_SSM + SPI_CR1_SSI +
SPI_CR1_MSTR ; // /2
SPI1->CR1 |= SPI_CR1_SPE ; // Enable SPI
while((SPI1->SR&SPI_SR_TXE)==0); // Wait till transmitter empty
SPI1->DR = 0x4F ; // Just show
something '3'
}
Discussion
Refer to the Reference Manual for
the SPI Control Register 1, for which we merely selected the Master mode with
the highest baud rate and internally set the NSS pin.
These configurations should be done
before enabling the SPI in the next statement.
Segment Display
This is a program to check the
connection for each segment.
//
P16SPI24.c SPI1 Segment Display
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
int
main(){
int i,j ;
char k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = 0x49
;
// Test with another
code
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=0;k<=7;k++){
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = 0x01<<k ;
for(i=0;i<=500;i++){for(j=0;j<=1000;j++);}
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
}
Digit Display
Once the SPI is working, digits can
be displayed easily.
The following program displays the
digits.
//
P16SPI27.c SPI1 Digit Display
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
char
CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
int
main(){
int i,j ;
char k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = 0x49
;
// Test with another
code
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
while(1){
for(k=0;k<=9;k++){
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = CODE7[k] ;
for(i=0;i<=1000;i++){for(j=0;j<=1000;j++);}
}
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
}
LCD 2x16
An andm(alpha-numeric dot-matrix)
2x16 LCD can be added, to be connected after the 74HC164. An unused pin PC12 is
connected to the ‘E’ signal pin of the LCD. The following program is only an
initial test to check the connection and the initialization.
Since software delays are used for
the LCD routines, the program may not work properly if the clock is
subsequently changed to a much higher value.
//
P17LCD22.c LCD 2x16 Initial Test
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
char
CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
void
LCDsend(unsigned char K);
int
main(){
unsigned int i,j ;
char k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = 0x49
;
// Test with another
code
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set bit6
LCDsend(0x30);
LCDsend(0x30);
LCDsend(0x30);
LCDsend(0x30);
LCDsend(0x30);
LCDsend(0x20);
LCDsend(0x20);
LCDsend(0x80);
LCDsend(0x00);
LCDsend(0xF0);
LCDsend(0x00);
LCDsend(0x60);
LCDsend(0x00);
LCDsend(0x10);
for(i=0;i<=50000;i++); // Give time for clear screen
LCDsend(0x80);
LCDsend(0x90);
LCDsend(0x51);
LCDsend(0x31);
// 0x53 is S
while(1){
for(k=0;k<=9;k++){
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = CODE7[k] ;
for(i=0;i<=1000;i++){for(j=0;j<=1000;j++);}
}
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR |=
1<<LCDE ; //
E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgC(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
// LCD 2x16
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
}
LCD Functions
It is of course necessary to put the
LCD procedures into functions. This is quite easy as we can copy from previous
examples.
//
P17LCD24.c LCD 2x16 Functions
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
char
CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
void
LCDcmd(unsigned char M);
void
LCDdata(unsigned char D);
int
main(){
int i,j ;
char k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = 0x49 ;
// Test with another
code
GPIOB->ODR &= ~(1<<7)
; // Clear bit7
GPIOB->ODR |= 1<<6
; // Set
bit6
LCDcmd(0xC3);
//
Lower line Column 3
LCDdata('$'); // Test
with a character
while(1){
for(k=0;k<=9;k++){
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = CODE7[k] ;
for(i=0;i<=1000;i++){for(j=0;j<=1000;j++);}
}
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR
|= 1<<LCDE
; // E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
LCDdata(unsigned char D){
LCDsend((D&0xF0)|0x01);
LCDsend((D<<4)|0x01);
}
void
LCDcmd(unsigned char M){
LCDsend(M&0xF0);
LCDsend(M<<4);
}
void
LCDinit(void){
unsigned int i ;
LCDcmd(0x33); // LCD Initialisation
LCDcmd(0x33);
LCDcmd(0x32); // 4-wire mode
LCDcmd(0x28); // 2 x 16
LCDcmd(0x0F);
LCDcmd(0x06);
LCDcmd(0x01);
for(i=0;i<=50000;i++); // Give time for clear
screen
LCDcmd(0x87);
LCDdata('S');
LCDdata('T');
LCDdata('M');
LCDdata('3');
LCDdata('2');
LCDdata('L');
LCDcmd(0xC0);
LCDdata('>');
}
void
PinCfgB(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgA(char PIN, char MODE, char FUNC){
. . .
}
void
PinCfgC(char PIN, char MODE, char FUNC){
. . .
}
void
SystemInit(void){
. . .
// LCD 2x16
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
}
Interrupt
Timing is usually provided by timers
which usually operate with interrupts.
This was quite difficult. The
interrupt involves the NVIC of the Cortex-M3 part.
This posting is preliminary. The
program works but further study may be required.
This example with Timer9 producing
an interrupt interval of 1 ms is used to check the accuracy of the HSI clock.
Although an interval of 1 second can be similarly produced, it is likely that
the 1-ms interval is commonly required and more flexible. Besides, the 1-second
interval is more likely required for a real time clock which can be provided by
the RTC function of the microcontroller, to be tested a bit later.
//
P21T9I22.c Interrupt Timer9 1 ms (HSI)
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of
Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
unsigned
char CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
unsigned
int ms ;
unsigned
char sec ;
void
LCDcmd(unsigned char M);
void
LCDdata(unsigned char D);
int
main(void){
unsigned int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
GPIOB->ODR
&= ~(1<<7)
; // Clear
bit7
GPIOB->ODR
|= 1<<6
; //
Set bit6
LCDcmd(0xC3); // Lower
line Column 3
LCDdata('$'); // Test
with a character
NVIC->ISER[0] |= 1<<25 ;
// TIM9_IRQn = 25
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR
|= 1<<7 ; //
Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR
&= ~(1<<7) ; // Clear
PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
TIM9_IRQHandler(void){
TIM9->SR &=
~TIM_SR_UIF; // Clear
Timer9 Flag
ms++;
if(ms>=1000){
ms = 0 ;
sec++;
if(sec>=10) sec = 0 ;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = CODE7[sec] ;
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR
|= 1<<LCDE
; // E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
LCDdata(unsigned char D){
LCDsend((D&0xF0)|0x01);
LCDsend((D<<4)|0x01);
}
void
LCDcmd(unsigned char M){
LCDsend(M&0xF0);
LCDsend(M<<4);
}
void
LCDinit(void){
unsigned int i ;
LCDcmd(0x33); // LCD Initialisation
LCDcmd(0x33);
LCDcmd(0x32); // 4-wire mode
LCDcmd(0x28); // 2 x 16
LCDcmd(0x0F);
LCDcmd(0x06);
LCDcmd(0x01);
for(i=0;i<=50000;i++); // Give time for clear
screen
LCDcmd(0x87);
LCDdata('S');
LCDdata('T');
LCDdata('M');
LCDdata('3');
LCDdata('2');
LCDdata('L');
LCDcmd(0xC0);
LCDdata('>');
}
void
PinCfgA(char PIN, char MODE, char FUNC){
GPIOA->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOA->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOA->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOA->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOA->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOA->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOA->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOA->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOA->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOA->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOA->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOA->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
GPIOB->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOB->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOB->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOB->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOB->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOB->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOB->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOB->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOB->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOB->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOB->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOB->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgC(char PIN, char MODE, char FUNC){
GPIOC->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOC->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOC->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOC->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOC->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOC->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOC->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOC->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOC->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOC->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOC->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOC->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
SystemInit(void){
// WS=1
FLASH->ACR |= FLASH_ACR_ACC64
; // 64-bit access
while( (FLASH->ACR&FLASH_ACR_ACC64)==0) ;
FLASH->ACR |= FLASH_ACR_PRFTEN
; // Prefetch
FLASH->ACR |= FLASH_ACR_LATENCY
; // 1 wait state
while( (FLASH->ACR&FLASH_ACR_LATENCY)==0) ;
// HSI
RCC->CR
|= RCC_CR_HSION ;
while( (RCC->CR&RCC_CR_HSIRDY)==0 ); // Wait
till HSI Ready
RCC->CFGR &=
~RCC_CFGR_SW
;
// Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSI
;
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait
till HSI
// LEDs PB7(Green), PB6(Blue)
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0);
PinCfgB(6,1,0);
// PA0 Configuration Input Push Button
RCC->AHBENR |=
RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0);
// SPI1
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
RCC->APB2ENR |=
RCC_APB2ENR_SPI1EN ;
PinCfgA(5,2,5);
// PIN=5, Output SCK
PinCfgA(12,2,5); // PIN=12,
Output MOSI
PinCfgA(11,2,5); // PIN=11, Input
MISO
SPI1->CR1 = SPI_CR1_SSM +
SPI_CR1_SSI + SPI_CR1_MSTR ; // /2
SPI1->CR1
|= SPI_CR1_SPE ; // Enable SPI
while((SPI1->SR&SPI_SR_TXE)==0); // Wait till transmitter empty
SPI1->DR = 0x4F
;
// Just show something
'3'
// andmLCD
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
LCDinit();
// Timer9 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN ;
TIM9->PSC = 15 ;
// Prescaler=15+1=16 --> 1 MHz
TIM9->ARR = 999
;
// 1000 --> 1 ms
TIM9->CR1 |= TIM_CR1_CEN ;
// Enable Timer9
TIM9->SR &=
~TIM_SR_UIF;
// Clear Timer9 Flag
TIM9->DIER |= TIM_DIER_UIE ; // Enable Timer9
Interrupt
ms = 0 ;
sec = 0 ;
}
Discussion
The interrupt is working with an
accuracy of about 1%, provided by the internal clock.
The interrupt vector TIM9_IRQn = 25 is obtained from the stm32l1xx.h file.
The name of the interrupt service
routine TIM9_IRQHandler is obtained from the startup_stm32l1xx_md.s file, line 103 or 211.
RTC
The RTC requires quite a complicated
procedure to configure. But it is a time-of-day clock/calendar function. First
it was configured and tested, by polling, to be counting properly. However the
interrupt processing took quite a while. There was no interrupt. Careful
reading of the header file revealed that the interrupt involves an EXTI Line.
Further reading of an example helped to solve the problem. The codes may be
hard to understand without referring to the manual.
The Timer9 interrupt is retained
with an empty interrupt service routine.
Initially the RTC was tested using
just the 7-segment display. The program below makes use of the andm LCD to
display the digital time.
//
P22RTC23.c RTC Real Time Clock
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of
Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
unsigned
char CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
unsigned
int ms ;
unsigned
char sec ;
void
LCDcmd(unsigned char M);
void
LCDdata(unsigned char D);
int
main(void){
unsigned int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
GPIOB->ODR
&= ~(1<<7)
; // Clear
bit7
GPIOB->ODR
|= 1<<6
; //
Set bit6
LCDcmd(0xC3); // Lower
line Column 3
LCDdata('$'); // Test
with a character
NVIC->ISER[0] |= 1<<3
;
//
RTC_WKUP_IRQn = 3
NVIC->ISER[0] |= 1<<25 ;
// TIM9_IRQn = 25
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR
|= 1<<7 ; //
Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR
&= ~(1<<7) ; // Clear
PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
RTC_WKUP_IRQHandler(void){
unsigned char S ;
unsigned long t ;
RTC->ISR
&= ~RTC_ISR_WUTF; //
Clear WakeUpTimer Flag
EXTI->PR
= EXTI_PR_PR20 ; //
Clear EXTI Line 20 pending
t = RTC->TR ;
LCDcmd(0xC1);
S = (t>>20)&0x03 ;
LCDdata(0x30+S); // HT
S = (t>>16)&0x0F ;
LCDdata(0x30+S);
// HU
LCDdata(':');
S = (t>>12)&0x07 ;
LCDdata(0x30+S);
S = (t>>8)&0x0F ;
LCDdata(0x30+S);
LCDdata(':');
S = (t>>4)&0x07 ;
LCDdata(0x30+S);
S = (t>>0)&0x0F ;
LCDdata(0x30+S);
LCDcmd(0xCE); // Move
out cursor
}
void
TIM9_IRQHandler(void){
TIM9->SR &=
~TIM_SR_UIF; // Clear
Timer9 Flag
ms++;
if(ms>=1000){
ms = 0 ;
sec++;
if(sec>=10) sec = 0 ;
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR
|= 1<<LCDE
; // E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
LCDdata(unsigned char D){
LCDsend((D&0xF0)|0x01);
LCDsend((D<<4)|0x01);
}
void
LCDcmd(unsigned char M){
LCDsend(M&0xF0);
LCDsend(M<<4);
}
void
LCDinit(void){
unsigned int i ;
LCDcmd(0x33); // LCD Initialisation
LCDcmd(0x33);
LCDcmd(0x32); // 4-wire mode
LCDcmd(0x28); // 2 x 16
LCDcmd(0x0F);
LCDcmd(0x06);
LCDcmd(0x01);
for(i=0;i<=50000;i++); // Give time for clear
screen
LCDcmd(0x87);
LCDdata('S');
LCDdata('T');
LCDdata('M');
LCDdata('3');
LCDdata('2');
LCDdata('L');
LCDcmd(0xC0);
LCDdata('>');
}
void
PinCfgA(char PIN, char MODE, char FUNC){
GPIOA->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOA->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOA->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOA->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOA->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOA->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOA->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOA->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOA->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOA->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOA->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOA->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
GPIOB->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOB->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOB->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOB->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOB->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOB->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOB->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOB->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOB->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOB->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOB->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOB->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgC(char PIN, char MODE, char FUNC){
GPIOC->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOC->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOC->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOC->OSPEEDR &= ~(0x03<<(2*PIN))
; // Speed mask
GPIOC->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOC->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOC->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOC->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOC->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOC->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOC->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOC->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
SystemInit(void){
// WS=1
FLASH->ACR |= FLASH_ACR_ACC64
; // 64-bit access
while( (FLASH->ACR&FLASH_ACR_ACC64)==0) ;
FLASH->ACR |= FLASH_ACR_PRFTEN
; // Prefetch
FLASH->ACR |= FLASH_ACR_LATENCY
; // 1 wait state
while( (FLASH->ACR&FLASH_ACR_LATENCY)==0) ;
// HSI
RCC->CR
|= RCC_CR_HSION ;
while( (RCC->CR&RCC_CR_HSIRDY)==0 ); // Wait
till HSI Ready
RCC->CFGR &=
~RCC_CFGR_SW
;
// Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSI
;
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait
till HSI
// LEDs PB7(Green), PB6(Blue)
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0);
PinCfgB(6,1,0);
// PA0 Configuration Input Push Button
RCC->AHBENR |=
RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0);
// SPI1
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
RCC->APB2ENR |=
RCC_APB2ENR_SPI1EN ;
PinCfgA(5,2,5);
// PIN=5, Output SCK
PinCfgA(12,2,5); // PIN=12,
Output MOSI
PinCfgA(11,2,5); // PIN=11, Input
MISO
SPI1->CR1 = SPI_CR1_SSM +
SPI_CR1_SSI + SPI_CR1_MSTR ; // /2
SPI1->CR1
|= SPI_CR1_SPE ; // Enable SPI
while((SPI1->SR&SPI_SR_TXE)==0); // Wait till transmitter empty
SPI1->DR = 0x4F
;
// Just show something
'3'
// andmLCD
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
LCDinit();
// Timer9 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN ;
TIM9->PSC = 15 ;
// Prescaler=15+1=16 --> 1 MHz
TIM9->ARR = 999
;
// 1000 --> 1 ms
TIM9->CR1 |= TIM_CR1_CEN ;
// Enable Timer9
TIM9->SR &=
~TIM_SR_UIF;
// Clear Timer9 Flag
TIM9->DIER |= TIM_DIER_UIE ; // Enable Timer9
Interrupt
ms = 0 ;
sec = 0 ;
// RTC Configuration
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
PWR->CR
|= PWR_CR_DBP ;
RCC->CSR
|= RCC_CSR_LSEON ;
while( (RCC->CSR&RCC_CSR_LSERDY)==0 ); // Wait till LSE
Ready
RCC->CSR
&= ~RCC_CSR_RTCSEL
; // Mask
RCC->CSR
|= RCC_CSR_RTCSEL_LSE
;
RCC->CSR
|= RCC_CSR_RTCEN ;
RTC->WPR
= 0xCA
;
// Removing
RTC->WPR
= 0x53
;
// Write Protect
RTC->ISR
|= RTC_ISR_INIT ;
while( (RTC->ISR&RTC_ISR_INITF)==0 );
RTC->TR
= 0x123451
; // 12:34:51
RTC->DR
|= RTC_DR_YT_0 + RTC_DR_YU_0
; // 11.01.01
while( (RTC->ISR&RTC_ISR_INITS)==0 );
RTC->ISR
&= ~RTC_ISR_INIT ;
RTC->CR
&= ~RTC_CR_WUTE ;
while( (RTC->ISR&RTC_ISR_WUTWF)==0 );
RTC->WUTR
= 0
;
// 1 second interrupt
RTC->CR
&= ~RTC_CR_WUCKSEL ;
RTC->CR
|= RTC_CR_WUCKSEL_2 ; // ck_spre
RTC->CR
|= RTC_CR_WUTE ;
RTC->ISR
&=
~RTC_ISR_WUTF;
// Clear WakeUpTimer Flag
RTC->CR
|= RTC_CR_WUTIE ;
EXTI->PR
= EXTI_PR_PR20 ;
EXTI->IMR
|= EXTI_IMR_MR20 ;
EXTI->RTSR
|= EXTI_RTSR_TR20
;
}
This looks rather lengthy. But it is
just expanding from the previous programs.
ADC
Before we do this, take note that
the ADC10 input pin (used) is at Port C bit 0, which is also SEG18 for the
on-board LCD. The connection has to be removed when the on-board LCD is to be
used.
Let us add just a couple of
applications. Measuring an analogue voltage is of course very useful. As this
is a 12-bit ADC, the result has to come out on the LCD. But the LCD will be
used to display the time also, in the interrupt routine. So, to avoid
complication, we shall measure the voltage in the interrupt routine, after
displaying the time. It is not good to spend too long a time in an interrupt
service routine. But this can be solved later.
//
P24ADC10.c ADC10
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of
Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
unsigned
char CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
unsigned
int ms ;
unsigned
char sec ;
void
LCDcmd(unsigned char M);
void
LCDdata(unsigned char D);
int
main(void){
unsigned int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
GPIOB->ODR
&= ~(1<<7)
; // Clear
bit7
GPIOB->ODR
|= 1<<6
; //
Set bit6
LCDcmd(0xC3); // Lower
line Column 3
LCDdata('$'); // Test
with a character
NVIC->ISER[0] |= 1<<3
;
//
RTC_WKUP_IRQn = 3
NVIC->ISER[0] |= 1<<25 ;
// TIM9_IRQn = 25
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR
|= 1<<7 ; //
Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR
&= ~(1<<7) ; // Clear
PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
RTC_WKUP_IRQHandler(void){
unsigned char S,H ;
unsigned long t,A ;
RTC->ISR
&= ~RTC_ISR_WUTF; //
Clear WakeUpTimer Flag
EXTI->PR
= EXTI_PR_PR20 ; //
Clear EXTI Line 20 pending
t = RTC->TR ;
LCDcmd(0xC1);
S = (t>>20)&0x03 ;
LCDdata(0x30+S); // HT
S = (t>>16)&0x0F ;
LCDdata(0x30+S);
// HU
LCDdata(':');
S = (t>>12)&0x07 ;
LCDdata(0x30+S);
S = (t>>8)&0x0F ;
LCDdata(0x30+S);
LCDdata(':');
S = (t>>4)&0x07 ;
LCDdata(0x30+S);
S = (t>>0)&0x0F ;
LCDdata(0x30+S);
LCDcmd(0xCA); // Move
out cursor
// ADC10 Measurement
while( (ADC1->SR&ADC_SR_ADONS)==0 );
ADC1->CR2
|= ADC_CR2_SWSTART ;
while( (ADC1->SR&ADC_SR_EOC)==0 );
A = ADC1->DR ;
LCDcmd(0xCA);
LCDdata('0');
LCDdata('x');
H = (A>>8)&0x0F ;
if(H<=9)LCDdata(0x30+H);
else LCDdata('A'+H-10);
H =
(A>>4)&0x0F ;
if(H<=9)LCDdata(0x30+H);
else
LCDdata('A'+H-10);
H =
(A>>0)&0x0F;
if(H<=9)LCDdata(0x30+H);
else LCDdata('A'+H-10);
LCDdata('
'); // Move out cursor
}
void
TIM9_IRQHandler(void){
TIM9->SR &=
~TIM_SR_UIF; // Clear
Timer9 Flag
ms++;
if(ms>=1000){
ms = 0 ;
sec++;
if(sec>=10) sec = 0 ;
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR
|= 1<<LCDE
; // E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
LCDdata(unsigned char D){
LCDsend((D&0xF0)|0x01);
LCDsend((D<<4)|0x01);
}
void
LCDcmd(unsigned char M){
LCDsend(M&0xF0);
LCDsend(M<<4);
}
void
LCDinit(void){
unsigned int i ;
LCDcmd(0x33); // LCD Initialisation
LCDcmd(0x33);
LCDcmd(0x32); // 4-wire mode
LCDcmd(0x28); // 2 x 16
LCDcmd(0x0F);
LCDcmd(0x06);
LCDcmd(0x01);
for(i=0;i<=50000;i++); // Give time for clear
screen
LCDcmd(0x87);
LCDdata('S');
LCDdata('T');
LCDdata('M');
LCDdata('3');
LCDdata('2');
LCDdata('L');
LCDcmd(0xC0);
LCDdata('>');
}
void
PinCfgA(char PIN, char MODE, char FUNC){
GPIOA->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOA->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOA->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOA->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOA->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOA->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOA->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOA->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOA->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOA->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOA->AFR[1]
&= ~(0x0F<<(4*(PIN-8))); // AFR
mask
GPIOA->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
GPIOB->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOB->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOB->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOB->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOB->OSPEEDR |=
0x01<<(2*PIN) ; // 2
MHz(01)
GPIOB->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOB->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOB->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOB->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOB->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOB->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOB->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgC(char PIN, char MODE, char FUNC){
GPIOC->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOC->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOC->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOC->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOC->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOC->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOC->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOC->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOC->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOC->AFR[0]
|= FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOC->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOC->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
SystemInit(void){
// WS=1
FLASH->ACR |= FLASH_ACR_ACC64
; // 64-bit access
while( (FLASH->ACR&FLASH_ACR_ACC64)==0) ;
FLASH->ACR |= FLASH_ACR_PRFTEN
; // Prefetch
FLASH->ACR |= FLASH_ACR_LATENCY
; // 1 wait state
while( (FLASH->ACR&FLASH_ACR_LATENCY)==0) ;
// HSI
RCC->CR
|= RCC_CR_HSION ;
while( (RCC->CR&RCC_CR_HSIRDY)==0 ); // Wait
till HSI Ready
RCC->CFGR &=
~RCC_CFGR_SW
;
// Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSI
;
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait
till HSI
// LEDs PB7(Green), PB6(Blue)
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0);
PinCfgB(6,1,0);
// PA0 Configuration Input Push Button
RCC->AHBENR |=
RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0);
// SPI1
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
RCC->APB2ENR |=
RCC_APB2ENR_SPI1EN ;
PinCfgA(5,2,5);
// PIN=5, Output SCK
PinCfgA(12,2,5); // PIN=12,
Output MOSI
PinCfgA(11,2,5); // PIN=11, Input
MISO
SPI1->CR1 = SPI_CR1_SSM +
SPI_CR1_SSI + SPI_CR1_MSTR ; // /2
SPI1->CR1
|= SPI_CR1_SPE ; // Enable SPI
while((SPI1->SR&SPI_SR_TXE)==0); // Wait till transmitter empty
SPI1->DR = 0x4F
;
// Just show something
'3'
// andmLCD
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
LCDinit();
// Timer9 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN ;
TIM9->PSC = 15 ;
// Prescaler=15+1=16 --> 1 MHz
TIM9->ARR = 999
;
// 1000 --> 1 ms
TIM9->CR1 |= TIM_CR1_CEN ;
// Enable Timer9
TIM9->SR &=
~TIM_SR_UIF;
// Clear Timer9 Flag
TIM9->DIER |= TIM_DIER_UIE ; // Enable Timer9
Interrupt
ms = 0 ;
sec = 0 ;
// RTC
Configuration
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
PWR->CR
|= PWR_CR_DBP ;
RCC->CSR
|= RCC_CSR_LSEON ;
while( (RCC->CSR&RCC_CSR_LSERDY)==0 ); // Wait till LSE
Ready
RCC->CSR
&= ~RCC_CSR_RTCSEL
; // Mask
RCC->CSR
|= RCC_CSR_RTCSEL_LSE
;
RCC->CSR
|= RCC_CSR_RTCEN ;
RTC->WPR
= 0xCA
;
// Removing
RTC->WPR
= 0x53
;
// Write Protect
RTC->ISR
|= RTC_ISR_INIT ;
while( (RTC->ISR&RTC_ISR_INITF)==0 );
RTC->TR
= 0x123451
; // 12:34:51
RTC->DR
|= RTC_DR_YT_0 + RTC_DR_YU_0
; // 11.01.01
while( (RTC->ISR&RTC_ISR_INITS)==0 );
RTC->ISR
&= ~RTC_ISR_INIT ;
RTC->CR
&= ~RTC_CR_WUTE ;
while( (RTC->ISR&RTC_ISR_WUTWF)==0 );
RTC->WUTR
= 0
;
// 1 second interrupt
RTC->CR
&= ~RTC_CR_WUCKSEL ;
RTC->CR
|= RTC_CR_WUCKSEL_2 ; // ck_spre
RTC->CR
|= RTC_CR_WUTE ;
RTC->ISR
&= ~RTC_ISR_WUTF;
// Clear WakeUpTimer Flag
RTC->CR
|= RTC_CR_WUTIE ;
EXTI->PR
= EXTI_PR_PR20 ;
EXTI->IMR
|= EXTI_IMR_MR20 ;
EXTI->RTSR
|= EXTI_RTSR_TR20
;
// ADC Configuration ADC10
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(0,3,0); // PIN=0,
ADC10
RCC->APB2ENR |=
RCC_APB2ENR_ADC1EN ;
while( (ADC1->SR&ADC_SR_ADONS)!=0 );
ADC1->CR2
|= ADC_CR2_ADON ;
while( (ADC1->SR&ADC_SR_ADONS)==0 );
ADC1->SQR5 = 10
;
// Channel 10
}
DAC
Then of course we can just do the
DAC. We will just connect an LED to the DAC1 output at PA4. Then we measure the
ADC10 voltage from the potentiometer(trimmer), scale up this voltage and send
it to the DAC1 data register. It would then appear that the
potentiometer(trimmer) is controlling the brightness of the LED. There is just
a small problem. The response is very bad, because the ADC10 is measured at
1-second intervals.
//
P25DAC01.c DAC1
#include
"stm32l1xx.h"
#define
Button 0x01 // Button is bit 0 of
Port A
#define
LCDE
12
// LCD_E is bit 12 of Port C
unsigned
char CODE7[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x0A};
unsigned
int ms ;
unsigned
char sec ;
void
LCDcmd(unsigned char M);
void
LCDdata(unsigned char D);
int
main(void){
unsigned int i,k ;
while( (GPIOA->IDR&Button)==0 ); // If '0', wait for pressed
while( (GPIOA->IDR&Button)!=0 ); // If '1', wait for released
GPIOB->ODR
&= ~(1<<7)
; // Clear
bit7
GPIOB->ODR
|= 1<<6
; //
Set bit6
LCDcmd(0xC3); // Lower
line Column 3
LCDdata('$'); // Test
with a character
NVIC->ISER[0] |= 1<<3
;
//
RTC_WKUP_IRQn = 3
NVIC->ISER[0] |= 1<<25 ;
// TIM9_IRQn = 25
while(1){
for(k=5;k<=2995;k++){
GPIOB->ODR
|= 1<<7 ; //
Set PB7
for(i=0;i<=k;i++);
GPIOB->ODR
&= ~(1<<7) ; // Clear
PB7
for(i=0;i<=(3000-k);i++);
}
}
}
void
RTC_WKUP_IRQHandler(void){
unsigned char S,H ;
unsigned long t,A ;
RTC->ISR
&= ~RTC_ISR_WUTF; //
Clear WakeUpTimer Flag
EXTI->PR
= EXTI_PR_PR20 ; //
Clear EXTI Line 20 pending
t = RTC->TR ;
LCDcmd(0xC1);
S = (t>>20)&0x03 ;
LCDdata(0x30+S); // HT
S = (t>>16)&0x0F ;
LCDdata(0x30+S);
// HU
LCDdata(':');
S = (t>>12)&0x07 ;
LCDdata(0x30+S);
S = (t>>8)&0x0F ;
LCDdata(0x30+S);
LCDdata(':');
S = (t>>4)&0x07 ;
LCDdata(0x30+S);
S = (t>>0)&0x0F ;
LCDdata(0x30+S);
LCDcmd(0xCA); // Move
out cursor
// ADC10 Measurement
while( (ADC1->SR&ADC_SR_ADONS)==0 );
ADC1->CR2
|= ADC_CR2_SWSTART ;
while( (ADC1->SR&ADC_SR_EOC)==0 );
A = ADC1->DR ;
LCDcmd(0xCA);
LCDdata('0');
LCDdata('x');
H = (A>>8)&0x0F ;
if(H<=9)LCDdata(0x30+H);
else LCDdata('A'+H-10);
H = (A>>4)&0x0F
;
if(H<=9)LCDdata(0x30+H);
else
LCDdata('A'+H-10);
H =
(A>>0)&0x0F;
if(H<=9)LCDdata(0x30+H);
else LCDdata('A'+H-10);
LCDdata('
'); // Move out cursor
DAC->DHR12R1 = A/2.0 + 0x800
; // Scale up
and Send to DAC1 output
}
void
TIM9_IRQHandler(void){
TIM9->SR &=
~TIM_SR_UIF; // Clear
Timer9 Flag
ms++;
if(ms>=1000){
ms = 0 ;
sec++;
if(sec>=10) sec = 0 ;
}
}
void
LCDsend(unsigned char K){
unsigned int i;
while((SPI1->SR&SPI_SR_TXE)==0);
SPI1->DR = K ;
while((SPI1->SR&SPI_SR_BSY)!=0); // Wait if still busy
for(i=0;i<=1000;i++);
GPIOC->ODR
|= 1<<LCDE
; // E = 1
for(i=0;i<=1000;i++);
GPIOC->ODR
&= ~(1<<LCDE) ; // E = 0
for(i=0;i<=5000;i++);
}
void
LCDdata(unsigned char D){
LCDsend((D&0xF0)|0x01);
LCDsend((D<<4)|0x01);
}
void
LCDcmd(unsigned char M){
LCDsend(M&0xF0);
LCDsend(M<<4);
}
void
LCDinit(void){
unsigned int i ;
LCDcmd(0x33); // LCD Initialisation
LCDcmd(0x33);
LCDcmd(0x32); // 4-wire mode
LCDcmd(0x28); // 2 x 16
LCDcmd(0x0F);
LCDcmd(0x06);
LCDcmd(0x01);
for(i=0;i<=50000;i++); // Give time for clear
screen
LCDcmd(0x87);
LCDdata('S');
LCDdata('T');
LCDdata('M');
LCDdata('3');
LCDdata('2');
LCDdata('L');
LCDcmd(0xC0);
LCDdata('>');
}
void
PinCfgA(char PIN, char MODE, char FUNC){
GPIOA->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOA->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOA->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOA->OSPEEDR &= ~(0x03<<(2*PIN))
; // Speed mask
GPIOA->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOA->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOA->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOA->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOA->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOA->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOA->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOA->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgB(char PIN, char MODE, char FUNC){
GPIOB->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOB->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOB->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOB->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOB->OSPEEDR |= 0x01<<(2*PIN)
; // 2 MHz(01)
GPIOB->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOB->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOB->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOB->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOB->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOB->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOB->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
PinCfgC(char PIN, char MODE, char FUNC){
GPIOC->MODER &=
~(0x03<<(2*PIN)) ; // Mode mask
GPIOC->MODER |=
MODE<<(2*PIN) ; // 0=i; 1=O; 2=AltFun;
3=Analog
GPIOC->OTYPER &= ~(1<<PIN) ;
//
PushPull(0)
GPIOC->OSPEEDR &=
~(0x03<<(2*PIN)) ; // Speed mask
GPIOC->OSPEEDR |=
0x01<<(2*PIN) ; // 2 MHz(01)
GPIOC->PUPDR &=
~(0x03<<(2*PIN)) ; // PUPD mask
GPIOC->PUPDR |= (0x00<<(2*PIN)) ;
// No PUPD(00)
GPIOC->ODR
&= ~(1<<PIN)
;
// Clear PIN
if(PIN<=7){
GPIOC->AFR[0] &= ~(0x0F<<(4*PIN)) ;
// AFR mask
GPIOC->AFR[0] |=
FUNC<<(4*PIN) ;
// Alt Func
} else{
GPIOC->AFR[1] &=
~(0x0F<<(4*(PIN-8))); // AFR mask
GPIOC->AFR[1] |=
FUNC<<(4*(PIN-8)) ; // Alt Func
}
}
void
SystemInit(void){
// WS=1
FLASH->ACR |= FLASH_ACR_ACC64
; // 64-bit access
while( (FLASH->ACR&FLASH_ACR_ACC64)==0) ;
FLASH->ACR |= FLASH_ACR_PRFTEN
; // Prefetch
FLASH->ACR |= FLASH_ACR_LATENCY
; // 1 wait state
while( (FLASH->ACR&FLASH_ACR_LATENCY)==0) ;
// HSI
RCC->CR
|= RCC_CR_HSION ;
while( (RCC->CR&RCC_CR_HSIRDY)==0 ); // Wait
till HSI Ready
RCC->CFGR &=
~RCC_CFGR_SW
;
// Clock Switch Mask
RCC->CFGR |= RCC_CFGR_SW_HSI
;
while( (RCC->CFGR&RCC_CFGR_SWS)!=RCC_CFGR_SWS_HSI ); // Wait
till HSI
// LEDs PB7(Green), PB6(Blue)
RCC->AHBENR |= RCC_AHBENR_GPIOBEN ;
PinCfgB(7,1,0);
PinCfgB(6,1,0);
// PA0 Configuration Input Push Button
RCC->AHBENR |=
RCC_AHBENR_GPIOAEN ;
PinCfgA(0,0,0);
// SPI1
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
RCC->APB2ENR |=
RCC_APB2ENR_SPI1EN ;
PinCfgA(5,2,5);
// PIN=5, Output SCK
PinCfgA(12,2,5); // PIN=12,
Output MOSI
PinCfgA(11,2,5); // PIN=11, Input
MISO
SPI1->CR1 = SPI_CR1_SSM +
SPI_CR1_SSI + SPI_CR1_MSTR ; // /2
SPI1->CR1
|= SPI_CR1_SPE ; // Enable SPI
while((SPI1->SR&SPI_SR_TXE)==0); // Wait till transmitter empty
SPI1->DR = 0x4F ;
// Just show something
'3'
// andmLCD
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(12,1,0); // PIN=LCDE=12,
Output LCDE
LCDinit();
// Timer9 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN ;
TIM9->PSC = 15 ;
// Prescaler=15+1=16 --> 1 MHz
TIM9->ARR = 999
;
// 1000 --> 1 ms
TIM9->CR1 |= TIM_CR1_CEN ;
// Enable Timer9
TIM9->SR &=
~TIM_SR_UIF;
// Clear Timer9 Flag
TIM9->DIER |= TIM_DIER_UIE ; // Enable Timer9
Interrupt
ms = 0 ;
sec = 0 ;
// RTC Configuration
RCC->APB1ENR |= RCC_APB1ENR_PWREN ;
PWR->CR
|= PWR_CR_DBP ;
RCC->CSR
|= RCC_CSR_LSEON ;
while( (RCC->CSR&RCC_CSR_LSERDY)==0 ); // Wait till LSE
Ready
RCC->CSR
&= ~RCC_CSR_RTCSEL
; // Mask
RCC->CSR
|= RCC_CSR_RTCSEL_LSE
;
RCC->CSR
|= RCC_CSR_RTCEN ;
RTC->WPR
= 0xCA
;
// Removing
RTC->WPR
= 0x53
;
// Write Protect
RTC->ISR
|= RTC_ISR_INIT ;
while( (RTC->ISR&RTC_ISR_INITF)==0 );
RTC->TR
= 0x123451
; // 12:34:51
RTC->DR
|= RTC_DR_YT_0 + RTC_DR_YU_0
; // 11.01.01
while( (RTC->ISR&RTC_ISR_INITS)==0 );
RTC->ISR
&= ~RTC_ISR_INIT ;
RTC->CR
&= ~RTC_CR_WUTE ;
while( (RTC->ISR&RTC_ISR_WUTWF)==0 );
RTC->WUTR
= 0
;
// 1 second interrupt
RTC->CR
&= ~RTC_CR_WUCKSEL ;
RTC->CR
|= RTC_CR_WUCKSEL_2 ; // ck_spre
RTC->CR
|= RTC_CR_WUTE ;
RTC->ISR
&=
~RTC_ISR_WUTF;
// Clear WakeUpTimer Flag
RTC->CR
|= RTC_CR_WUTIE ;
EXTI->PR
= EXTI_PR_PR20 ;
EXTI->IMR
|= EXTI_IMR_MR20 ;
EXTI->RTSR
|= EXTI_RTSR_TR20
;
// ADC Configuration ADC10
RCC->AHBENR |= RCC_AHBENR_GPIOCEN ;
PinCfgC(0,3,0); // PIN=0,
ADC10
RCC->APB2ENR |=
RCC_APB2ENR_ADC1EN ;
while( (ADC1->SR&ADC_SR_ADONS)!=0 );
ADC1->CR2
|= ADC_CR2_ADON ;
while( (ADC1->SR&ADC_SR_ADONS)==0 );
ADC1->SQR5 = 10
;
// Channel 10
// DAC Configuration DAC1 at PA4
RCC->AHBENR |=
RCC_AHBENR_GPIOAEN ;
PinCfgA(4,3,0); // PIN=0,
ADC10
RCC->APB1ENR
|= RCC_APB1ENR_DACEN ;
DAC->CR
|= DAC_CR_EN1
; // DAC1 ON
}
That ends the classical study on the
STM32L-Discovery.