diff --git a/ADC_PWM_Assignment_Part0_Tejas.png b/ADC_PWM_Assignment_Part0_Tejas.png new file mode 100644 index 0000000..6c6b886 Binary files /dev/null and b/ADC_PWM_Assignment_Part0_Tejas.png differ diff --git a/ADC_PWM_Assignment_Part1_Tejas.png b/ADC_PWM_Assignment_Part1_Tejas.png new file mode 100644 index 0000000..cd11367 Binary files /dev/null and b/ADC_PWM_Assignment_Part1_Tejas.png differ diff --git a/projects/lpc40xx_freertos/.vscode/settings.json b/projects/lpc40xx_freertos/.vscode/settings.json new file mode 100644 index 0000000..9cd751f --- /dev/null +++ b/projects/lpc40xx_freertos/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "cstdio": "c" + } +} \ No newline at end of file diff --git a/projects/lpc40xx_freertos/assignment_include.h b/projects/lpc40xx_freertos/assignment_include.h new file mode 100644 index 0000000..6ad53c7 --- /dev/null +++ b/projects/lpc40xx_freertos/assignment_include.h @@ -0,0 +1,24 @@ +/** + * @file assignment_include.h + * @author Tejas Pidkalwar + * @date 19th Sept 2020 + * @brief This file maintains the macro defined to enable specific assignment implementation for + * execution, along with subparts of assignment enabling feature + */ +#ifndef _ASSIGNMENT_INCLUDE_ +#define _ASSIGNMENT_INCLUDE_ + +/* + * This file defines macros corresponding each assignment. + * Enable only one assignment macro while compiling. + * This file also defines macros for sub parts corresponding to each assignment. + * Take care to enable only one part macro enabled while compiling. + */ + +// Assignment 5 +#define ADC_PWM_ASSIGNMENT +//#define PART0_ADC_PWM_ASSGNMT +//#define PART1_ADC_PWM_ASSGNMT +#define PART2_ADC_PWM_ASSGNMT + +#endif \ No newline at end of file diff --git a/projects/lpc40xx_freertos/l3_drivers/adc.h b/projects/lpc40xx_freertos/l3_drivers/adc.h index 0854b8c..fd58c5a 100644 --- a/projects/lpc40xx_freertos/l3_drivers/adc.h +++ b/projects/lpc40xx_freertos/l3_drivers/adc.h @@ -24,3 +24,15 @@ void adc__initialize(void); * This starts conversion of one channel, and should not be used from multiple tasks */ uint16_t adc__get_adc_value(adc_channel_e channel_num); + +/** + * This function is responsible for enabling burst mode by configuring CR register + */ +void adc__enable_burst_mode(void); + +/** + * Read the ADC channel values while in burst mode for a given channel + * @param channel_number The channel number of ADC to read the values + * @return read ADC values + */ +uint16_t adc__get_channel_reading_with_burst_mode(adc_channel_e channel_number); diff --git a/projects/lpc40xx_freertos/l3_drivers/sources/adc.c b/projects/lpc40xx_freertos/l3_drivers/sources/adc.c index 29461da..4fe93e7 100644 --- a/projects/lpc40xx_freertos/l3_drivers/sources/adc.c +++ b/projects/lpc40xx_freertos/l3_drivers/sources/adc.c @@ -16,7 +16,7 @@ void adc__initialize(void) { lpc_peripheral__turn_on_power_to(LPC_PERIPHERAL__ADC); const uint32_t enable_adc_mask = (1 << 21); - LPC_ADC->CR = enable_adc_mask; + LPC_ADC->CR |= enable_adc_mask; const uint32_t max_adc_clock = (12 * 1000UL * 1000UL); // 12.4Mhz : max ADC clock in datasheet for lpc40xx const uint32_t adc_clock = clock__get_peripheral_clock_hz(); @@ -51,3 +51,18 @@ uint16_t adc__get_adc_value(adc_channel_e channel_num) { return result; } + +void adc__enable_burst_mode(void) { + // Set the BURST bit in control register + LPC_ADC->CR |= (1 << 16); + + // Clear Start bits from control register to be in burst mode + LPC_ADC->CR &= ~(0b111 << 24); +} + +uint16_t adc__get_channel_reading_with_burst_mode(uint8_t channel_number) { + LPC_ADC->CR &= ~(0xFF); + LPC_ADC->CR |= (1 << 2); + uint32_t status = LPC_ADC->DR[2]; + return ((status >> 4) & 0x0FFF); +} diff --git a/projects/lpc40xx_freertos/l3_drivers/sources/pwm1.c b/projects/lpc40xx_freertos/l3_drivers/sources/pwm1.c index 41833e9..1391479 100644 --- a/projects/lpc40xx_freertos/l3_drivers/sources/pwm1.c +++ b/projects/lpc40xx_freertos/l3_drivers/sources/pwm1.c @@ -3,6 +3,7 @@ #include "clock.h" #include "lpc40xx.h" #include "lpc_peripherals.h" +#include "stdio.h" void pwm1__init_single_edge(uint32_t frequency_in_hertz) { const uint32_t pwm_channel_output_enable_mask = 0x3F; @@ -16,6 +17,9 @@ void pwm1__init_single_edge(uint32_t frequency_in_hertz) { } const uint32_t match_reg_value = (clock__get_peripheral_clock_hz() / valid_frequency_in_hertz); + // TODO: Remove before commiting + fprintf(stderr, "PWM valid freq is %ld and match reg value is %ld \n", valid_frequency_in_hertz, match_reg_value); + // MR0 holds the value that Timer Counter should count upto // This will get us the desired PWM pulses per second // Ex: If CPU freq = 10Hz, desired frequency = 2Hz @@ -53,5 +57,10 @@ void pwm1__set_duty_cycle(pwm1_channel_e pwm1_channel, float duty_cycle_in_perce break; } +#ifdef PART0_ADC_PWM_ASSGNMT + const uint32_t mr1_reg_val = LPC_PWM1->MR1; + fprintf(stderr, "Current Value of MR0 and MR1 registers are : %ld and %ld \n", mr0_reg_val, mr1_reg_val); + +#endif LPC_PWM1->LER |= (1 << (pwm1_channel + 1)); ///< Enable Latch Register } diff --git a/projects/lpc40xx_freertos/l5_application/main.c b/projects/lpc40xx_freertos/l5_application/main.c index 3194d1d..921abaf 100644 --- a/projects/lpc40xx_freertos/l5_application/main.c +++ b/projects/lpc40xx_freertos/l5_application/main.c @@ -9,15 +9,136 @@ #include "periodic_scheduler.h" #include "sj2_cli.h" +#ifdef ADC_PWM_ASSIGNMENT + +#include "adc.h" +#include "lpc_peripherals.h" +#include "pwm1.h" +#include "semphr.h" + +#endif + static void create_blinky_tasks(void); static void create_uart_task(void); static void blink_task(void *params); static void uart_task(void *params); +#ifdef ADC_PWM_ASSIGNMENT + +QueueHandle_t adc_to_pwm_q; + +void pwm_task() { + pwm1__init_single_edge(1000); + + // Set port 2 pin 0 as PWM1 channel 0 as an PWM output + gpio__construct_with_function(2, 0, GPIO__FUNCTION_1); + + // Initialize duty cycle to 50% + pwm1__set_duty_cycle(PWM1__2_0, 50); + +#ifdef PART0_ADC_PWM_ASSGNMT + uint8_t percent = 0; + bool count_down = false; + while (1) { + pwm1__set_duty_cycle(PWM1__2_0, percent); + + if (count_down) { + percent -= 5; + if (percent <= 0) + count_down = false; + } else { + percent += 5; + if (percent > 100) + count_down = true; + } + vTaskDelay(50); + } +#elif defined(PART2_ADC_PWM_ASSGNMT) + uint8_t percent = 0; + // Set port 2 pin 1 as PWM1 channel 1 as an PWM output + gpio__construct_with_function(2, 1, GPIO__FUNCTION_1); + + // Set port 2 pin 2 as PWM1 channel 2 as an PWM output + gpio__construct_with_function(2, 2, GPIO__FUNCTION_1); + uint16_t max_adc_value = 4096; + uint16_t convertedValue = 0; + while (1) { + if (xQueueReceive(adc_to_pwm_q, &convertedValue, 100)) { + percent = (convertedValue * 100 / max_adc_value); + pwm1__set_duty_cycle(PWM1__2_0, percent); + pwm1__set_duty_cycle(PWM1__2_1, ((percent + 30) % 100)); + pwm1__set_duty_cycle(PWM1__2_2, ((percent + 60) % 100)); + } + } +#endif +} + +void adc_task() { + adc__initialize(); + adc__enable_burst_mode(); + + // Make Port 0 Pin 25 functionality as ADC0[2] + gpio__construct_with_function(0, 25, GPIO__FUNCTION_1); + + // Set mode and analog input bits in control register + LPC_IOCON->P0_25 &= ~(0b11 << 3); + + LPC_IOCON->P0_25 &= ~(1 << 7); + uint16_t convertedValue = 0; +#ifdef PART1_ADC_PWM_ASSGNMT + + double current_voltage_value = 0; + double max_adc_value = 4096; + while (1) { + // Read the ADC value in burst mode + convertedValue = adc__get_channel_reading_with_burst_mode(ADC__CHANNEL_2); + current_voltage_value = (convertedValue / max_adc_value) * (3.3); + fprintf(stderr, "Current Pot Value is %d\n", convertedValue); + fprintf(stderr, "Current voltage level is %f \n", current_voltage_value); + vTaskDelay(1000); + } +#elif defined(PART2_ADC_PWM_ASSGNMT) + while (1) { + // Read the ADC value in burst mode + convertedValue = adc__get_channel_reading_with_burst_mode(ADC__CHANNEL_2); + // Send the ADC value to PWM channels + xQueueSend(adc_to_pwm_q, &convertedValue, 0); + vTaskDelay(100); + } +#endif +} + +#endif + int main(void) { + +#ifdef ADC_PWM_ASSIGNMENT + +#if defined(PART0_ADC_PWM_ASSGNMT) || defined(PART2_ADC_PWM_ASSGNMT) + + xTaskCreate(pwm_task, "pwm", 1024 / sizeof(void *), NULL, PRIORITY_LOW, NULL); + +#endif + +#if defined(PART1_ADC_PWM_ASSGNMT) || defined(PART2_ADC_PWM_ASSGNMT) + + xTaskCreate(adc_task, "adc", 1024 / sizeof(void *), NULL, PRIORITY_LOW, NULL); + +#endif + +#ifdef PART2_ADC_PWM_ASSGNMT + + adc_to_pwm_q = xQueueCreate(10, sizeof(uint16_t)); + +#endif + +#else + create_blinky_tasks(); create_uart_task(); +#endif + puts("Starting RTOS"); vTaskStartScheduler(); // This function never returns unless RTOS scheduler runs out of memory and fails diff --git a/projects/lpc40xx_freertos/lpc40xx.h b/projects/lpc40xx_freertos/lpc40xx.h index 2f138cb..a6b9e54 100644 --- a/projects/lpc40xx_freertos/lpc40xx.h +++ b/projects/lpc40xx_freertos/lpc40xx.h @@ -27,6 +27,8 @@ #ifndef __LPC407x_8x_H__ #define __LPC407x_8x_H__ +#include "assignment_include.h" + /* * ========================================================================== * ---------- Interrupt Number Definition -----------------------------------