Lý thuyết và code kèm theo tương đối đầy đủ các bài thực hành GPIO, SYSTICK, PWM và ADC sách lập trình vi điều khiển STM32L152 Embedded Systems with ARM CortexM Microcontrollers in Assembly Language and C (Third Edition – Dr Yifeng Zhu) nội dung bao gồm: bật tắt led bằng nút nhấn, bật tắt led bằng delay SysTick Timer, tăng giảm độ sáng đèn bằng xung PWM, thay đổi điện áp đầu ra bằng cách sử dụng triết áp và hiển thị kết qua lên màn hình LCD ADC
Trang 1LẬP TRÌNH VI ĐIỀU KHIỂN STM32L152
(Sách Embedded Systems with ARM Cortex-M Microcontrollers
in Assembly Language and C (Third Edition) – Dr Yifeng Zhu )
Nội dung: Lý thuyết và mã nguồn các bài thực hành trên chip STM32L152RC các bài GPIO, SysTick Timer, PWM và ADC
I BÀI THỰC HÀNH CHƯƠNG 12 –GPIO (GENERAL PURPOSE INPUT OUTPUT) - BẬT TẮT ĐÈN BẰNG NÚT NHẤN
Lab 1: Interfacing Push-button and LED Instructor: Prof Yifeng Zhu Spring
2015
SƠ ĐỒ KHỐI:
Trang 2MÃ NGUỒN BẬT TẮT LED BẲNG BUTTON
Yêu cầu : Thiết lập các chân PB.6, PB.7 là ngõ ra nối với các led xanh dương và xanh lá, chân PA.0 là đầu vào kết nối với nút nhấn của vi điều khiển, khi nhấn nút các đèn xanh dương và xanh lá thay đổi trạng thái
Trang 3}
void LED_BLUE(){ // set pin x I/O mode as GP Output
GPIOB->MODER &=~(0x03<<(2*6)); // reset state
GPIOB->MODER |=(0x01<<(2*6)); // set pin x as a digital output 01
GPIOB->OTYPER &=~(1<<6); // output type push-pull 0
GPIOB->OSPEEDR &=~(0x03<<(2*6)); // set I/O speed // set mark
GPIOA->MODER &=~(0x03); // set pin 0 I/O mode as GP input 00
GPIOA->OTYPER &=~(0x1); //output type pushpull 0
GPIOA->OSPEEDR &=~(0x03); //mark
Trang 4GPIO_Clock_Enable(); //enable port A and B
BUTTON_A(); //config PA0
GPIOB->ODR |=1<<7; //turn off a yellow led
GPIOB->ODR &=~(1<<6); //turn on a blue led
while (1){
if ((GPIOA->IDR &1) ==1) { // button is pressed
GPIOB->ODR ^=1<<6;
//change status of led 6
GPIOB->ODR ^=1<<7;
//change status of led 7
for (delay = 0; delay < 100000; delay++){} //delay for a range time
Trang 5ECE 271 Microcomputer Architecture and Applications - Lab 5: System Timer
(SysTick) Instructor: Prof Yifeng Zhu Spring 2015
A : LÝ THUYẾT
- SysTick là một chức năng định thời được tích hợp vào phần cứng
- Là một bộ đếm xuống 24 bit - có giá trị nạp vào tối đa là 2^24 -1
- Cứ sau một khoảng thời gian khi bộ đếm đếm về 0 thì sinh ra ngắt, dựa vào đó gọi một tác vụ xử lý ngắt
- Xung nhịp được lấy từ xung clock của lõi ARM
Công thức:
Interrupt period = (1 + SysTick_LOAD)/ Clock Frequence
Chu kỳ ngắt = (1 + Giá trị RELOAD)/ tần số xung nhịp
Trong đó giá trị lớn nhất của SysTick_LOAD = 2^24 -1 = 16777215
B : SƠ ĐÒ KHỐI VÀ MÃ NGUỒN BẬT TẮT LED BẲNG BUTTON
SƠ ĐỒ KHỐI:
Trang 6MÃ NGUỒN : Bật tắt led bằng delay
#include <stdint.h>
#include "stm32l1xx.h"
uint32_t TimingDelay;
void GPIOB_Clock_Enable()
Trang 8//NVIC_SetPriority (SysTick_IRQn, (1<< NVIC_PRIO_BITS)-1);
// Reset the SysTick counter value
SysTick->VAL = 0;
//Select processor clock
Trang 9//1 = processor clock; 0 = external clock
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE;//0x04
//Enable SysTick IRQ and SysTick Timer
SysTick->CTRL |= SysTick_CTRL_ENABLE;// 0x01
// Enables SysTick exception request
// 1 = counting down zero asserts the SysTick exception request
// 2 = counting down zero do not asserts the SysTick exception request
Trang 10SysTick_Initialize(4194); // 4194 xung nhip
GPIOB->ODR &= ~(1<<6); // set pb 6 low
GPIOB->ODR |= 1<<7; // set pb 7 high
ECE 271 Microcomputer Architecture and Applications Lab 6: Pulse Width Modulation
Instructor: Prof Yifeng Zhu Spring 2015
Trang 11PHẦN I : LÝ THUYẾT
- PWM (Pulse with modulation) - điều chế độ rộng xung :
- Là một kỹ thuật số đơn giản dùng để điều khiển giá trị của điện áp ra tải
- Sử dụng một xung chữ nhật ngắn để nhanh chóng bật tắt nguồn điện thế ON/ OFF, tạo giá trị điện
1 Prescaler : hệ số chia : có giá trị trong khoản từ 0 -> 65535
2 Counter modes : chế độ đếm - đếm tiến, đếm lùi hoặc nửa tiến nửa lùi
3 Counter period : chu kỳ đếm
Trang 16//OC1M=110 for PMW Mode 1 output on channel 1
TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // OC1M = 110
TIM4->CCMR1 |= TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; // OC2M = 110
TIM4->CCMR1 |= TIM_CCMR1_OC1PE; //out put 1 preload enable- 0x0008
TIM4->CCMR1 |= TIM_CCMR1_OC2PE;
TIM4->CR1 |= TIM_CR1_ARPE; // reload preload enable - 0x0080
Trang 17Auto-TIM4->CCER |= TIM_CCER_CC1E; // Enable output for channel 1- 0x001
Trang 18// nTime : specifies the delay time length
Trang 19TIM4->CCR2 = brightness_1; // Set brightness for chanel 2
for(i=0; i<1000; i++); // A short delay
Trang 20//Configure GPIO pins
Trang 21TIM4->PSC = 2097000/1000-1;
//Set the auto-reload value: upcounting (0->ARR), downcounting (ARR->0)
TIM4->ARR = 1000;
TIM4->CCR1 = 500;
//Set PWM mode 1 or mode 2 on chanel 1, channel 2
TIM4->CCMR1 |= TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1; //bit
Trang 22+ Độ tiêu tốn năng lượng
Độ phân giải càng cao thì tốc độ thực của tín hiệu càng cao nhưng tốn nhiều bộ nhớ để lưu dữ liệu
- 3 chức năng chính:
+ Lấy mẫu : rời rạc hóa về mặt thời gian
+ Lượng tử hóa : rời rạc hóa trục biên độ
+ Mã hóa: chuyển mẫu sang mã nhị phân
Trang 23Có ba thông số cơ bản :
1 Prescaler : hệ số chia : có giá trị trong khoản từ 0 -> 65535
2 Counter modes : chế độ đếm - đếm tiến, đếm lùi hoặc nửa tiến nửa lùi
3 Counter period : chu kỳ đếm
B : SƠ ĐÒ KHỐI VÀ MÃ NGUỒN
SƠ ĐỒ KHỐI:
Trang 24Lập vô tận
Trang 25// This code is for the book "Embedded Systems with ARM Cortex-M3
// Microcontrollers in Assembly Language and C, Yifeng Zhu,
Trang 26// ISBN-10: 0982692625
// @attension
// This code is provided for education purpose The author shall not be
// held liable for any direct, indirect or consequential damages, for any
// reason whatever More information can be found from book website:
* STM32L1 Discovery Kit Pin Connections (STM32L152RBT6 or STM32L152RCT6)
* USER Pushbutton < -> PA.0 (clock: RCC_AHBENR_GPIOAEN)
* RESET Pushbutton < -> RESET
* Green LED (LD3) < -> PB.7 (clock: RCC_AHBENR_GPIOBEN)
* Blue LED (LD4) < -> PB.6 (clock: RCC_AHBENR_GPIOBEN)
* Touch Sensors < -> 6 pins, PA.6,7 (group 2), PB.0,1 (group 3), PC.4,5 (group 9)
* LCD (24 segments)< -> 28 pins, PA.1,2,3,8,9,10,15,
* PB.3,4,5,8,9,10,11,12,13,14,15
Trang 27* PC.0,1,2,3,6,7,8,9,10,11
* ST Link < -> PA.13,14
* Boot 1 < -> PB.2
* Freely available pins: PA.5, PA.11, PA.12, PC.12, PD.2
* A GPIO pin is 5V tolerant and can sink or source up to 8 mA
************************************************************************
*********************************
*/
#define bool _Bool
volatile uint32_t TimingDelay = 0;
char buff[30];
void SysTick_Initialize(uint32_t ticks){
SysTick->CTRL = 0; // Disable SysTick IRQ and SysTick counter
RCC->ICSCR &= ~(0x07<<13); // Interrupt Control and State Register
RCC->ICSCR |= 0x06<<13; // Set MSIRANGE as 110 (4.194 Mhz)
RCC->CR |= 0x01<<8; // Set MSION
while(!(RCC->CR & (0x01<<9))); // Wait for MSIRDY
SysTick->LOAD = ticks - 1; // Set reload register
//NVIC_SetPriority(SysTick_IRQn, (1<< NVIC_PRIO_BITS) - 1);
Trang 29void LCD_WriteChar(uint8_t* ch, bool point, bool colon, uint8_t position);
static void LCD_Conv_Char_Seg(uint8_t* c, bool point, bool colon, uint8_t* digit);
void LCD_DisplayStringScroll(uint8_t* ptr, bool direction, uint32_t delay); // My
function
void ADCInit(void); // My function
void LCD_Clock_Init(void){
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // Power interface clock enable
PWR->CR |= PWR_CR_DBP; // Disable Backup Domain write protection
RCC->CSR |= RCC_CSR_RTCSEL_LSI; // LSI oscillator clock used as RTC clock
//LSI clock varies due to frequency dispersion
//RCC->CSR |= RCC_CSR_RTCSEL_LSE; // LSE oscillator clock used as RTC clock
RCC->CSR |= RCC_CSR_RTCEN; // RTC clock enable
/* Disable the write protection for RTC registers */
RTC->WPR = 0xCA; // RTC write protection register (WPR)
RTC->WPR = 0x53; // Write "0xCA" and "0x53" to unlock the write protection
// Wait until MSI clock ready
while((RCC->CR & RCC_CR_MSIRDY) == 0); // MSI Ready Flag is set by hardware
/* Enable comparator clock LCD */
RCC->APB1ENR |= RCC_APB1ENR_LCDEN;
Trang 30RCC->AHBENR |= 0x01<<0; // Enable the clock to GPIO port A
RCC->AHBENR |= 0x01<<1; // Enable the clock to GPIO port B
RCC->AHBENR |= 0x01<<2; // Enable the clock to GPIO port C
//Set port A (pin 1,2,3,8,9,10,15), B (pin 3,4,5,8,9,10,11,12,13,14,15) and C (pin 0,1,2,3,6,7,8,9,10,11) as Alternative Function (AF)
GPIOA->MODER &= ~(0x03<<(2*1) | 0x03<<(2*2) | 0x03<<(2*3) |
0x03<<(2*8) | 0x03<<(2*9) | 0x03<<(2*10) | 0x03<<(2*15));
Trang 32GPIOB->AFR[1] |= 0x0B<<(4*(8-8)) | 0x0B<<(4*(9-8)) | 0x0B<<(4*(10-8)) | 0x0B<<(4*(11-8)) | 0x0B<<(4*(12-8)) | 0x0B<<(4*(13-8)) | 0x0B<<(4*(14-8)) |
LCD->FCR &= ~LCD_FCR_PON;
Trang 33LCD->FCR |= LCD_FCR_PON_0 | LCD_FCR_PON_1 | LCD_FCR_PON_2; // Set the pulse on period to 111
LCD->CR |= LCD_CR_MUX_SEG; // Enable the mux segment
LCD->CR &= ~LCD_CR_VSEL; // Select internal voltage as LCD voltage source
while((LCD->SR & LCD_SR_FCRSR) == 0); // Wait until FCRSF flag of LCD_SR is set
LCD->CR |= LCD_CR_LCDEN; // Enable the LCD
while((LCD->SR & LCD_SR_ENS) == 0); // Wait until the LCD is enabled
while((LCD->SR & LCD_SR_RDY) == 0); // Wait until the LCD booster is ready
}
void ADCInit(void){
RCC->CR |= RCC_CR_HSION; // Turn on HSI (High speed internal clock)
while((RCC->CR & RCC_CR_HSIRDY) == 0); // Wait until HSI is ready
Trang 34//RCC->AHBENR |= 0x01<<0; // Enable the clock to GPIO port A
GPIOA->MODER &= ~GPIO_MODER_MODER1;
GPIOA->MODER |= GPIO_MODER_MODER1_0 |
GPIO_MODER_MODER1_1; // Set pin 1 port A as analog
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // Turn on the ADC clock
ADC1->CR2 &= ~ADC_CR2_ADON; // Turn off the ADC conversion
ADC1->SQR1 &= ~(0x1F<<20); // Set the length of the regular channel sequence to 1
ADC1->SQR5 &= ~ADC_SQR5_SQ1;
ADC1->SQR5 |= 0x01; // Set channel 1 as the first conversion in regular sequence
ADC1->SMPR3 &= ~ADC_SMPR3_SMP4; // Configure the sample time register for channel 1 as 4 cycles
ADC1->CR1 |= ADC_CR1_EOCIE; // Enable End-Of-Conversion interrupt
ADC1->CR2 |= ADC_CR2_CONT; // Enable continuous conversion mode
ADC1->CR2 &= ~ADC_CR2_DELS;
Trang 35ADC1->CR2 |= ADC_CR2_DELS_0; // Configure delay selection as delayed until the coverted data have been read
NVIC_EnableIRQ(ADC1_IRQn); // Enable the interrupt of ADC1_IRQn in NVIC
NVIC_SetPriority(ADC1_IRQn, 5); // Configure the interrupt priority of ADC1_IRQn
ADC1->CR2 |= ADC_CR2_ADON; // Turn on the ADC conversion
ADC1->CR2 |= ADC_CR2_SWSTART; // Start the conversion of the regular channel
}
void ADC1_IRQHandler(void){ // ADC interrupt handler
if(ADC1->SR & ADC_SR_EOC){
// For a regular channel, check end of conversion (EOC) flag
sprintf(buff,"%d",ADC1->DR); // Save result on buff
LCD_DisplayString(buff);// show ADC result on lcd
Delay(200);
}
}
int main(void) {
Trang 36void LCD_DisplayStringScroll(uint8_t* ptr, bool direction, uint32_t delay) {
// ptr is pointer to string we want to display
Trang 37// direction = 1 if we want to scroll left to right and otherwise
// delay is the time between two scroll unit
LCD_Clear(); // Clear digits on LCD
for(i = 1; i <= 6; i++){ // Write to each digit of LCD
if(tptr+i-1 < endptr && tptr+i-1 >= ptr){ // If character we want to write is in range of string
LCD_WriteChar(tptr+i-1, 0, 0, i); // Write to LCD at position
i
}
}
Delay(delay); // Delay an amount of time
// If we scroll out of string, we start again Otherwise, increase or decrease tptr to make scroll effect
Trang 38tptr = direction ? (tptr+5 == ptr ? endptr : tptr-1) : (tptr == endptr-1 ? ptr-6 : tptr+1);
}
}
void LCD_Clear(void) {
// Implement your code here
while ((LCD->SR & LCD_SR_UDR) != 0); // Wait for Update Display Request Bit
LCD->RAM[0] &= 0;
LCD->RAM[2] &= 0;
LCD->RAM[4] &= 0;
LCD->RAM[6] &= 0;
LCD->SR |= LCD_SR_UDR; // Set the Update Display Request
while ((LCD->SR & LCD_SR_UDD) == 0); // Wait for Update Display Done Bit
}
/* Macros used for set/reset bar LCD bar */
#define BAR0_ON t_bar[1] |= 8
#define BAR0_OFF t_bar[1] &= ~8
#define BAR1_ON t_bar[0] |= 8
#define BAR1_OFF t_bar[0] &= ~8
Trang 39#define BAR2_ON t_bar[1] |= 2
#define BAR2_OFF t_bar[1] &= ~2
#define BAR3_ON t_bar[0] |= 2
#define BAR3_OFF t_bar[0] &= ~2
/* code for 'µ' character */
/* constant code for '*' character */
#define star 0xA0DD
/* constant code for '-' character */
#define C_minus 0xA000
/* constant code for '/' */
#define C_slatch 0x00c0
/* constant code for ° */
Trang 40#define C_percent_1 0xec00
/* constant code for small o */
Trang 41/* Constant table for cap characters 'A' > 'Z' */
const uint16_t CapLetterMap[26] = {
/* A B C D E F G H I */
Trang 42/* Constant table for number '0' > '9' */
const uint16_t NumberMap[10] = {
/* 0 1 2 3 4 5 6 7 8 9 */
0x5F00,0x4200,0xF500,0x6700,0xEa00,0xAF00,0xBF00,0x04600,0xFF00,0xEF00
};
void LCD_WriteChar(uint8_t* ch, bool point, bool colon, uint8_t position) {
uint8_t digit[4]; /* Digit frame buffer */
// Convert displayed character in segment in array digit
LCD_Conv_Char_Seg(ch, point, colon, digit);
// Wait until LCD Ready */
while ((LCD->SR & LCD_SR_UDR) != 0); // Wait for Update Display Request Bit
switch (position) {
/* Position 1 on LCD (Digit 1)*/
Trang 43LCD->RAM[0] |= ((digit[0]& 0x0c) << 26 ) | (digit[0]& 0x03) ; // 1G 1B 1M 1E
LCD->RAM[2] |= ((digit[1]& 0x0c) << 26 ) | (digit[1]& 0x03) ; // 1F 1A 1C 1D
LCD->RAM[4] |= ((digit[2]& 0x0c) << 26 ) | (digit[2]& 0x03) ; // 1Q 1K 1Col 1P
LCD->RAM[6] |= ((digit[3]& 0x0c) << 26 ) | (digit[3]& 0x03) ; // 1H 1J 1DP 1N
Trang 44LCD->RAM[4] |= ((digit[2]& 0x0c) << 24 )|((digit[2]& 0x02) << 6 )|((digit[2]& 0x01) << 2 ) ; // 2Q 2K 2Col 2P
LCD->RAM[6] |= ((digit[3]& 0x0c) << 24 )|((digit[3]& 0x02) << 6 )|((digit[3]& 0x01) << 2 ) ; // 2H 2J 2DP 2N
Trang 45LCD->RAM[4] &= 0xfff3efff;
LCD->RAM[6] &= 0xfff3efff;
LCD->RAM[0] |= ((digit[0]& 0x0c) << 16 ) | ((digit[0]& 0x03) << 12 ) ; // 5G 5B 5M 5E
Trang 46LCD->RAM[2] |= ((digit[1]& 0x0c) << 16 ) | ((digit[1]& 0x03) << 12 ) ; // 5F 5A 5C 5D
LCD->RAM[4] |= ((digit[2]& 0x0c) << 16 ) | ((digit[2]& 0x01) << 12 ) ; // 5Q 5K 5P
LCD->RAM[6] |= ((digit[3]& 0x0c) << 16 ) | ((digit[3]& 0x01) << 12 ) ; // 5H 5J 5N
Trang 47// Update the LCD display
// Set the Update Display Request
// Each time software modifies the LCD_RAM it must set the UDR bit to transfer the updated
// data to the second level buffer The UDR bit stays set until the end of the update and during
// this time the LCD_RAM is write protected