Microprocessor Lab
Fall 2017
Lecturer: S.L. Tsao @CS, NCTU Taiwan
Microprocessor Lab Final Project
-
Course Introduction
This course mainly focus on the embedded system understanding, and we use the ARM microarchitecture in this semester. -
Goal of this course
To briefly understand the ARM Assembly, how to write C code in the embedded developing board and make a small project from it by your own. -
Embedded board spec:
1.Nucleo STM32L476RG ARM Microarchitecture
2.Built-in 400MHz CPU (customizable frequency with internal clock settings provided)
3.GPIO Available
Detailed specs -
IDE for this course Eclipse AC6 System Workbench for STM32 where JRE7 is required in your system
1.Download from Here for Linux version
- Lab project and final project
There are 10 labs in this semester on a weekly basis, the first five focus on basic operation of ARM Assembly, namely the lab assignments are typically written in the ARM Assembly, while the last five labs are written in C language, but a embedded-like C language.
About my final project
- Project name: The RGB Ambient light.
- Features:
1.Fully-customizable RGB proportion control.
2.Color cycle speed controller (faster->original)
3.Light-sensitive resistor ADC ,detecting the background light for light quantity settings
GitHub repo for this final project Project demo video
Part0. Origin of this idea and preparation
-
Origin of this idea
I once have the fully-customizable dynamic RGB LED backlit keyboard SteelSeries APEXM 650
On account of having not much time in the end of semester(machine learning final project + compiler design final project.) My teammate and I decided to do it. -
Preparation of materials and tools for this project
1.A STM32L476RG Embedded board
2.Breadboard
3.4pin RGB LED*5 Click here
4.R1000 Resistor
5.Some Dupont cables
Part1. Circuit connection
1.Parallel connection of 5 RGB LEDs on the breadboard.
2.Connect separately to the GPIO pin on the STM32 providing the capability of PWM output.
3.Connect the keypad to the STM32.
4.Connect the light-sensitive resistor to GPIO with capability of ADC(Analog-Digital Converter).
5.The GPIO Connection is like.
6.Overall GPIO port configuration.
GPIOB->ASCR |= 0b1; //turn on the analog controller in PB0 void keypad_init()//keypad along with GPIO Init together { RCC->AHB2ENR |= 0b00000000000000000000000000000111; //open port A,B,C //10987654321098765432109876543210 GPIOC->MODER &= 0b11111111111111111111111100000000; //pc 3 2 1 0 as input of keypad GPIOC->MODER |= 0b00000000000000000000000001010101; GPIOC->PUPDR &= 0b11111111111111111111111100000000; GPIOC->PUPDR |= 0b00000000000000000000000001010101; GPIOC->OSPEEDR &= 0b11111111111111111111111100000000; GPIOC->OSPEEDR |= 0b00000000000000000000000001010101; GPIOC->ODR |= 0b00000000000000000000000000001111; //10987654321098765432109876543210 GPIOB->MODER &= 0b11111111111111110000000011111111; //pb 7 6 5 4 as output of keypad GPIOB->PUPDR &= 0b11111111111111110000000011111111; GPIOB->PUPDR |= 0b00000000000000001010101000000000; } void GPIO_init_AF() //GPIO Alternate Function Init { /***************pin and alternate function*************** * PB3 + AF1 which is corresponding to TIM2_CH2 RED * PA1 + AF2 which is corresponding to TIM5_CH2 GREEN * PA6 + AF2 which is corresponding to TIM3_CH1 BLUE ********************************************************/ //10987654321098765432109876543210 GPIOA->MODER &= 0b11111111111111111100111111110011; GPIOA->MODER |= 0b00000000000000000010000000001000; //PortA Pin //10987654321098765432109876543210 GPIOA->AFR[0] = 0b00000010000100000000000000100000; //PB3 TIM2_CH2 GPIOB->AFR[0] &= ~GPIO_AFRL_AFSEL3;//AFR[0] LOW GPIOB->AFR[0] |= (0b0001<<GPIO_AFRL_AFSEL3_Pos);//PB3 Alternate function mode }
Part2. Key idea of this project
The PWM(Pulse Width Modulation) Wiki
PWM cycle (HV/ALL) = The proportion where light lights, the longer HV lasts, the brighter of the certain part of RGB(either one) will do.
The Pulse Width can be used to simulate the analog output like this.
PWM Video
The same is true of other 3 colors, configuring with the following code and expanations.
Basic logic for this project
Initialize system -> PWM and timer configuration -> Presskey -> Color changing scheme along with ADC light intensity detection for power saving.
int keypad_value[4][4] = {{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
{12,13,14,15}};
0 red+
1 green+
2 blue+
3 cycle_speed+
4 red-
5 green-
6 blue-
7 customize mode(0 1 2 4 5 6 applicable)
8 only red
9 only green
10 only blue
11 light ADC mode
12 red+greren
13 green+blue
14 red+blue
15 off system, remember the last state, s.t. user configuration is not lost after shut down
Setup the PWM channel
Refer to this pdf for PWM channel-GPIO port configuration, each port has its corresponding PWM channel and built-in system clock, be sure to make it right!
More understanding and details are written in comments of the following source code.
Please refer to p.1006-1039 of this pdf to see how to config the PWM cycle with certain registers in timer.
void Timer_init() //Use 3 { // PA3 + AF1 which is corresponding to TIM2_CH1 // PA1 + AF2 which is corresponding to TIM5_CH2 // PA6 + AF2 which is corresponding to TIM3_CH1 RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; RCC->APB1ENR1 |= RCC_APB1ENR1_TIM3EN; RCC->APB1ENR1 |= RCC_APB1ENR1_TIM5EN; //setting for timer 2 TIM2->CR1 &= 0x0000; //p1027 Turned on the counter as the count up mode TIM2->ARR = (uint32_t)SECOND_SLICE;//Reload value TIM2->PSC = (uint32_t)COUNT_UP;//Prescaler TIM2->EGR = TIM_EGR_UG; //update the counter again p1035 //setting for timer 3 TIM3->CR1 &= 0x0000; //p1027 Turned on the counter as the count up mode TIM3->ARR = (uint32_t)SECOND_SLICE;//Reload value TIM3->PSC = (uint32_t)COUNT_UP;//Prescaler TIM3->EGR = TIM_EGR_UG;//Reinitialize the counter //setting for timer 5 TIM5->CR1 &= 0x0000; //p1027 Turned on the counter as the count up mode TIM5->ARR = (uint32_t)SECOND_SLICE;//Reload value TIM5->PSC = (uint32_t)COUNT_UP;//Prescaler TIM5->EGR = TIM_EGR_UG;//Reinitialize the counter } void PWM_channel_init() { /***********************setting for the TIM2_CH2 RED**************************/ // PB3 + AF1 which is corresponding to TIM2_CH2 RED //Output compare 2 mode TIM2->CCMR1 &= ~TIM_CCMR1_OC2M; //110: PWM mode 1: TIMx_CNT<TIMx_CCR2-->active, or inactive TIM2->CCMR1 |= (0b0110 << TIM_CCMR1_OC2M_Pos); //Output Compare 2 Preload Enable TIM2->CCMR1 &= ~TIM_CCMR1_OC2PE;//OCxPE //1: enable TIMx_CCR1 Preload TIM2->CCMR1 |= (0b1 << TIM_CCMR1_OC2PE_Pos); //enable auto reload pre-load TIM2->CR1 |= TIM_CR1_ARPE; //duty cycle initial 50 (CCR2/ARR) //TIM2->CCR2 = duty_cycle_R; //enable output compare TIM2->CCER |= TIM_CCER_CC2E; /***********************setting for the TIM5_CH2 GREEN**************************/ // PA1 + AF2 which is corresponding to TIM5_CH2 GREEN //Output compare 2 mode TIM5->CCMR1 &= ~TIM_CCMR1_OC2M; //110: PWM mode 1: TIMx_CNT<TIMx_CCR2-->active, or inactive TIM5->CCMR1 |= (0b0110 << TIM_CCMR1_OC2M_Pos); //Output Compare 2 Preload Enable TIM5->CCMR1 &= ~TIM_CCMR1_OC2PE;//OCxPE //1: enable TIMx_CCR1 Preload TIM5->CCMR1 |= (0b1 << TIM_CCMR1_OC2PE_Pos); //enable auto reload pre-load TIM5->CR1 |= TIM_CR1_ARPE; //duty cycle initial 50 (CCR2/ARR) //TIM5->CCR2 = duty_cycle_G; //enable output compare TIM5->CCER |= TIM_CCER_CC2E; /***********************setting for the TIM3_CH1 BLUE**************************/ // PA6 + AF2 which is corresponding to TIM3_CH1 BLUE //Output compare 2 mode TIM3->CCMR1 &= ~TIM_CCMR1_OC1M; //110: PWM mode 1: TIMx_CNT<TIMx_CCR2-->active, or inactive TIM3->CCMR1 |= (0b0110 << TIM_CCMR1_OC1M_Pos); //Output Compare 2 Preload Enable TIM3->CCMR1 &= ~TIM_CCMR1_OC1PE;//OCxPE //1: enable TIMx_CCR1 Preload TIM3->CCMR1 |= (0b1 << TIM_CCMR1_OC1PE_Pos); //enable auto reload pre-load TIM3->CR1 |= TIM_CR1_ARPE; //duty cycle initial 50 (CCR2/ARR) //TIM3->CCR1 = duty_cycle_B; //enable output compare TIM3->CCER |= TIM_CCER_CC1E; }
Part3. It's time to change the color.
-
Initialize to different duty cycle.
Each color has its own PWM cycle, by setting the PWM cycle differently, we will be able to interleave 3 colors and mixing them together since there pulse waves have "time shifting (or say phase shifting)" to each other.
#define RED_START 10 #define GREEN_START 91 #define BLUE_START 172 duty_cycle_R = RED_START; duty_cycle_G = GREEN_START; duty_cycle_B = BLUE_START; int main() { //use the time delay mode to make the interleaving and the color changing scheme fpu_enable(); keypad_init(); GPIO_init_AF(); Timer_init(); configureADC(); startADC(); duty_cycle_R = RED_START; duty_cycle_G = GREEN_START; duty_cycle_B = BLUE_START; cur_state = CYCLE_MODE; while(1) { PWM_channel_init(); chromatic_scheme(keypad_scan()); } return 0; }
-
Increase, decrease and cycle.
state_color is the state indicating whether to increase the pulse cycle or decrease, with an view to simulating the sin-wave-like phase wave.
void cycle_mode(int delay_time){ PWM_channel_init(); if (state_R){ if (duty_cycle_R > SECOND_SLICE){ state_R = 0; } else { duty_cycle_R += 20; } } else { if (duty_cycle_R < 20){ state_R = 1; } else { duty_cycle_R -= 20; } } if (state_G){ if (duty_cycle_G > SECOND_SLICE){ state_G = 0; } else { duty_cycle_G += 40; } } else { if (duty_cycle_G < 40){ state_G = 1; } else { duty_cycle_G -= 40; } } if (state_B){ if (duty_cycle_B > SECOND_SLICE){ state_B = 0; } else { duty_cycle_B += 50; } } else { if (duty_cycle_B < 50){ state_B = 1; } else { duty_cycle_B -= 50; } } set_timer(); start_timer(); delay_ms(delay_time); }
- Customizable mode
If it is in the customize mode, we are able to increase the proportion of color, to achieve that, just increase/decrease the duty cycle of that color. DELTA_VALUE is used to adjust the amount of duty cycle applied in PWM mode.
case 4: { if(duty_cycle_R > DELTA_VALUE) duty_cycle_R -= DELTA_VALUE; (or add the DELTA_VALUE) else duty_cycle_R = 0; break; }
Part4. More idea: the ADC of light-sensitive resistor
The Earth is now facing the serve global warming, it is vital for us to construct a power saving model, consequently Alice and I came out the idea of using the
ADC to detect the light intensity.
Concept of ADC configuration credit to my classmate's hackMD (Add later)
The stronger the intensity, the dimmer the light to be to saving the energy since this module is aimed for atmosphere night light.
ADC Code is here
case 11: { // light ADC mode for keypad key11 cur_state = LIGHT_MODE; break; } if(cur_state == LIGHT_MODE) { get_light_resistor(); light = 255-((resistor_value-REF_LIGHT)/10); duty_cycle_R = light; duty_cycle_G = light; duty_cycle_B = light; set_timer(); start_timer(); }
Part5. Done all.
Really thanks to my teammate chialice123 who helps me alot during the semester and in the final project making the project score over 90 and overall semester score to be 95 (93 original and +2 adjust), and vava24680 for teaching me some concepts of ADC configuration.