Monday 20 November 2017

zSTM32L152RB

20171121.2


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

  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=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
       LCDinit();
}

This would serve as a suitable template for most development work.




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.