From 3514b2576cf2e0bb09abc38eecbe1d9589200568 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sat, 6 Feb 2021 21:07:31 +0100 Subject: [PATCH] Get PWM working --- .gitignore | 8 ++ Descriptors.h | 115 ++++++++-------- Makefile | 2 +- event.c | 66 ++++++++++ event.h | 27 ++++ main.c | 49 ++++++- pwm.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++ pwm.h | 32 +++++ timer.c | 133 +++++++++++++++++++ timer.h | 25 ++++ 10 files changed, 749 insertions(+), 62 deletions(-) create mode 100644 .gitignore create mode 100644 event.c create mode 100644 event.h create mode 100644 pwm.c create mode 100644 pwm.h create mode 100644 timer.c create mode 100644 timer.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b294944 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +obj +*.bin +*.eep +*.elf +*.hex +*.lss +*.map +*.sym diff --git a/Descriptors.h b/Descriptors.h index 25ae8a5..8badb07 100644 --- a/Descriptors.h +++ b/Descriptors.h @@ -36,75 +36,74 @@ #ifndef _DESCRIPTORS_H_ #define _DESCRIPTORS_H_ - /* Includes: */ - #include + /* Includes: */ + #include - #include + #include - /* Macros: */ - /** Endpoint address of the CDC device-to-host notification IN endpoint. */ - #define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2) + /* Macros: */ + /** Endpoint address of the CDC device-to-host notification IN endpoint. */ + #define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2) - /** Endpoint address of the CDC device-to-host data IN endpoint. */ - #define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3) + /** Endpoint address of the CDC device-to-host data IN endpoint. */ + #define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3) - /** Endpoint address of the CDC host-to-device data OUT endpoint. */ - #define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4) + /** Endpoint address of the CDC host-to-device data OUT endpoint. */ + #define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4) - /** Size in bytes of the CDC device-to-host notification IN endpoint. */ - #define CDC_NOTIFICATION_EPSIZE 8 + /** Size in bytes of the CDC device-to-host notification IN endpoint. */ + #define CDC_NOTIFICATION_EPSIZE 8 - /** Size in bytes of the CDC data IN and OUT endpoints. */ - #define CDC_TXRX_EPSIZE 16 + /** Size in bytes of the CDC data IN and OUT endpoints. */ + #define CDC_TXRX_EPSIZE 16 - /* Type Defines: */ - /** Type define for the device configuration descriptor structure. This must be defined in the - * application code, as the configuration descriptor contains several sub-descriptors which - * vary between devices, and which describe the device's usage to the host. - */ - typedef struct - { - USB_Descriptor_Configuration_Header_t Config; + /* Type Defines: */ + /** Type define for the device configuration descriptor structure. This must be defined in the + * application code, as the configuration descriptor contains several sub-descriptors which + * vary between devices, and which describe the device's usage to the host. + */ + typedef struct + { + USB_Descriptor_Configuration_Header_t Config; - // CDC Control Interface - USB_Descriptor_Interface_t CDC_CCI_Interface; - USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header; - USB_CDC_Descriptor_FunctionalACM_t CDC_Functional_ACM; - USB_CDC_Descriptor_FunctionalUnion_t CDC_Functional_Union; - USB_Descriptor_Endpoint_t CDC_NotificationEndpoint; + // CDC Control Interface + USB_Descriptor_Interface_t CDC_CCI_Interface; + USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header; + USB_CDC_Descriptor_FunctionalACM_t CDC_Functional_ACM; + USB_CDC_Descriptor_FunctionalUnion_t CDC_Functional_Union; + USB_Descriptor_Endpoint_t CDC_NotificationEndpoint; - // CDC Data Interface - USB_Descriptor_Interface_t CDC_DCI_Interface; - USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; - USB_Descriptor_Endpoint_t CDC_DataInEndpoint; - } USB_Descriptor_Configuration_t; + // CDC Data Interface + USB_Descriptor_Interface_t CDC_DCI_Interface; + USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; + USB_Descriptor_Endpoint_t CDC_DataInEndpoint; + } USB_Descriptor_Configuration_t; - /** Enum for the device interface descriptor IDs within the device. Each interface descriptor - * should have a unique ID index associated with it, which can be used to refer to the - * interface from other descriptors. - */ - enum InterfaceDescriptors_t - { - INTERFACE_ID_CDC_CCI = 0, /**< CDC CCI interface descriptor ID */ - INTERFACE_ID_CDC_DCI = 1, /**< CDC DCI interface descriptor ID */ - }; + /** Enum for the device interface descriptor IDs within the device. Each interface descriptor + * should have a unique ID index associated with it, which can be used to refer to the + * interface from other descriptors. + */ + enum InterfaceDescriptors_t + { + INTERFACE_ID_CDC_CCI = 0, /**< CDC CCI interface descriptor ID */ + INTERFACE_ID_CDC_DCI = 1, /**< CDC DCI interface descriptor ID */ + }; - /** Enum for the device string descriptor IDs within the device. Each string descriptor should - * have a unique ID index associated with it, which can be used to refer to the string from - * other descriptors. - */ - enum StringDescriptors_t - { - STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ - STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ - STRING_ID_Product = 2, /**< Product string ID */ - }; + /** Enum for the device string descriptor IDs within the device. Each string descriptor should + * have a unique ID index associated with it, which can be used to refer to the string from + * other descriptors. + */ + enum StringDescriptors_t + { + STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ + STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ + STRING_ID_Product = 2, /**< Product string ID */ + }; - /* Function Prototypes: */ - uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, - const uint16_t wIndex, - const void** const DescriptorAddress) - ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); + /* Function Prototypes: */ + uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, + const uint16_t wIndex, + const void** const DescriptorAddress) + ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); #endif - diff --git a/Makefile b/Makefile index 248677c..b676da5 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ F_CPU = 16000000 F_USB = $(F_CPU) OPTIMIZATION = s TARGET = usbfanctrl -SRC = main.c Descriptors.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) $(LUFA_SRC_PLATFORM) +SRC = $(wildcard *.c) $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) $(LUFA_SRC_PLATFORM) LUFA_PATH = LUFA/LUFA CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig LD_FLAGS = diff --git a/event.c b/event.c new file mode 100644 index 0000000..3d30f45 --- /dev/null +++ b/event.c @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 01/2019 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License, * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include + +#include "event.h" + +/* *********************************************************************** */ + +static event_entry_t events[EVENT_COUNT]; +static volatile uint8_t event_in_idx; +static volatile uint8_t event_out_idx; + +/* *********************************************************************** */ + +void event_queue(uint8_t type, uint8_t num, uint16_t value) +{ + event_entry_t * p_event; + uint8_t sreg_save; + uint8_t idx; + + sreg_save = SREG; + cli(); + + idx = event_in_idx; + p_event = &events[idx++]; + if (p_event->type == EVENT_TYPE_EMPTY) + { + p_event->type = type; + p_event->num = num; + p_event->value = value; + event_in_idx = idx % EVENT_COUNT; + } + + SREG = sreg_save; +} /* event_queue */ + + +event_entry_t * event_get(void) +{ + return &events[event_out_idx]; +} /* event_get */ + + +void event_clear(void) +{ + uint8_t idx = event_out_idx; + events[idx++].type = EVENT_TYPE_EMPTY; + event_out_idx = idx % EVENT_COUNT; +} /* event_clear */ diff --git a/event.h b/event.h new file mode 100644 index 0000000..079e45f --- /dev/null +++ b/event.h @@ -0,0 +1,27 @@ +#ifndef __EVENT_H__ +#define __EVENT_H__ + +#include + +/* *********************************************************************** */ + +#define EVENT_COUNT 16 + +#define EVENT_TYPE_EMPTY 0x00 + +typedef struct event_entry_s +{ + uint8_t type; + uint8_t num; + uint16_t value; +} event_entry_t; + +/* *********************************************************************** */ + +void event_queue (uint8_t type, uint8_t num, uint16_t value); +event_entry_t * event_get (void); +void event_clear (void); + +/* *********************************************************************** */ + +#endif /* __EVENT_H__ */ diff --git a/main.c b/main.c index 8a2826b..88cca09 100644 --- a/main.c +++ b/main.c @@ -5,6 +5,9 @@ #include #include "Descriptors.h" +#include "event.h" +#include "pwm.h" +#include "timer.h" #include #include @@ -39,12 +42,52 @@ int main(void) { SetupHardware(); + timer_init(); + pwm_init(); + GlobalInterruptEnable(); + event_queue(EVENT_TYPE_PWM_COMMAND, 0, EVENT_VALUE_PWM_FADE_MAX); + event_queue(EVENT_TYPE_PWM_COMMAND, 1, EVENT_VALUE_PWM_FADE_MAX); + event_queue(EVENT_TYPE_PWM_COMMAND, 2, EVENT_VALUE_PWM_FADE_MAX); + for (;;) { - CDC_Task(); - USB_USBTask(); +// CDC_Task(); +// USB_USBTask(); + + timer_check(0); + + event_entry_t* p_event = event_get(); + if (p_event->type != EVENT_TYPE_EMPTY) + { + switch (p_event->type) + { + case EVENT_TYPE_PWM_COMMAND: + case EVENT_TYPE_PWM_VALUE: + pwm_event_handler(p_event); + break; + + case EVENT_TYPE_PWM_STATUS: + break; + + case EVENT_TYPE_TIMER_SET: + timer_event_handler(p_event); + break; + + case EVENT_TYPE_TIMER_ELAPSED: + if (p_event->num == EVENT_NUM_TIMER_PWM) + { + pwm_event_handler(p_event); + } + break; + + default: + break; + } + + event_clear(); + } } } @@ -58,7 +101,7 @@ void SetupHardware(void) /* Disable clock division */ clock_prescale_set(clock_div_1); - USB_Init(); +// USB_Init(); } /** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and diff --git a/pwm.c b/pwm.c new file mode 100644 index 0000000..a624fe7 --- /dev/null +++ b/pwm.c @@ -0,0 +1,354 @@ +/*************************************************************************** + * Copyright (C) 01/2019 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License, * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include +#include + +#include "pwm.h" +#include "timer.h" + + +/* *********************************************************************** */ + +#define PWM_TIM_16BIT 1 + +#define PWM_TIM_INIT() { \ + /* enable output for OC1A, OC1B, OC3A */ \ + DDRB |= (1<= PWM_TABLE_SIZE) + { + pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); + } + + if ((pwm_setpoint[channel] > pwm_index[channel]) && + (pwm_index[channel] < (PWM_TABLE_SIZE - 1)) + ) + { + pwm_index[channel]++; + check_setpoint = 1; + } + else if ((pwm_setpoint[channel] < pwm_index[channel]) && + (pwm_index[channel] > 0) + ) + { + pwm_index[channel]--; + check_setpoint = 1; + } + + /* setpoint reached, notify others */ + if (check_setpoint) + { + if (pwm_index[channel] == pwm_setpoint[channel]) + { + event_queue(EVENT_TYPE_PWM_STATUS, + channel, + pwm_setpoint[channel]); + } + else if (!pwm_fade_timer_running) + { + event_queue(EVENT_TYPE_TIMER_SET, + EVENT_NUM_TIMER_PWM, + PWM_TIMER_FADE_STEP_MS); + + pwm_fade_timer_running = 1; + } + } + + /* if PWM is zero, disable output */ + if (pwm_index[channel] == 0) + { + switch (channel) + { + case 0: + PWM_CH0_OFF(); + break; + + case 1: + PWM_CH1_OFF(); + break; + + case 2: + PWM_CH2_OFF(); + break; + + default: + break; + } + } + /* if PWM is max, enable output */ + else if (pwm_index[channel] == (PWM_TABLE_SIZE - 1)) + { + switch (channel) + { + case 0: + PWM_CH0_ON(); + break; + + case 1: + PWM_CH1_ON(); + break; + + case 2: + PWM_CH2_ON(); + break; + + default: + break; + } + } + /* else load new PWM into timer */ + else + { + switch (channel) + { + case 0: + PWM_CH0_PWM(PWM_TABLE_GET(pwm_index[0])); + break; + + case 1: + PWM_CH1_PWM(PWM_TABLE_GET(pwm_index[1])); + break; + + case 2: + PWM_CH2_PWM(PWM_TABLE_GET(pwm_index[2])); + break; + + default: + break; + } + } + + switch (channel) + { + case 0: + case 1: + /* disable timer if all channels are ON or OFF */ + if (PWM_TIM1_CHECK()) + { + PWM_TIM1_ENABLE(); + } + else + { + PWM_TIM1_DISABLE(); + } + break; + + case 2: + /* disable timer if all channels are ON or OFF */ + if (PWM_TIM3_CHECK()) + { + PWM_TIM3_ENABLE(); + } + else + { + PWM_TIM3_DISABLE(); + } + break; + } +} /* pwm_update */ + + +void pwm_event_handler(event_entry_t * p_event) +{ + if (p_event->type == EVENT_TYPE_PWM_COMMAND) + { + uint8_t channel = p_event->num; + + switch (p_event->value) + { + case EVENT_VALUE_PWM_KEEP: + pwm_setpoint[channel] = pwm_index[channel]; + break; + + /* increment PWM */ + case EVENT_VALUE_PWM_INC: + if (pwm_setpoint[channel] < (PWM_TABLE_SIZE - 1)) + { + pwm_setpoint[channel]++; + } + break; + + /* decrement PWM */ + case EVENT_VALUE_PWM_DEC: + if (pwm_setpoint[channel] > 0) + { + pwm_setpoint[channel]--; + } + break; + + /* fade to min */ + case EVENT_VALUE_PWM_FADE_MIN: + pwm_setpoint[channel] = 0; + break; + + /* fade to max */ + case EVENT_VALUE_PWM_FADE_MAX: + pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); + break; + + /* toggle between 0 and stored value */ + case EVENT_VALUE_PWM_TOGGLE: + if (pwm_setpoint[channel] > 5) + { + pwm_setpoint_save[channel] = pwm_setpoint[channel]; + pwm_setpoint[channel] = 0; + } + else + { + pwm_setpoint[channel] = pwm_setpoint_save[channel]; + } + break; + + default: + break; + } + + pwm_update(channel); + } + else if (p_event->type == EVENT_TYPE_PWM_VALUE) + { + uint8_t channel = p_event->num; + + if (p_event->value < PWM_TABLE_SIZE) + { + pwm_setpoint[channel] = p_event->value; + } + else + { + pwm_setpoint[channel] = (PWM_TABLE_SIZE - 1); + } + + pwm_update(channel); + } + else if ((p_event->type == EVENT_TYPE_TIMER_ELAPSED) && + (p_event->num == EVENT_NUM_TIMER_PWM) + ) + { + pwm_fade_timer_running = 0; + + pwm_update(0); + pwm_update(1); + pwm_update(2); + } +} /* pwm_event_handler */ + + +uint8_t pwm_get_sleep_mode(void) +{ + if (PWM_TIM1_RUNNING() || PWM_TIM3_RUNNING()) + { + return SLEEP_MODE_IDLE; + } + else + { + return SLEEP_MODE_PWR_DOWN; + } +} /* pwm_get_sleep_mode */ + + +void pwm_init(void) +{ + PWM_TIM_INIT(); +} /* pwm_init */ diff --git a/pwm.h b/pwm.h new file mode 100644 index 0000000..94b3171 --- /dev/null +++ b/pwm.h @@ -0,0 +1,32 @@ +#ifndef __PWM_H__ +#define __PWM_H__ + +#include +#include "event.h" + +/* *********************************************************************** */ + +#define EVENT_TYPE_PWM_COMMAND 0x04 +#define EVENT_TYPE_PWM_VALUE 0x05 +#define EVENT_TYPE_PWM_STATUS 0x06 + +#define EVENT_NUM_PWM_CH0 0x00 +#define EVENT_NUM_PWM_CH1 0x01 +#define EVENT_NUM_PWM_CH2 0x02 + +#define EVENT_VALUE_PWM_KEEP 0x00 +#define EVENT_VALUE_PWM_INC 0x01 +#define EVENT_VALUE_PWM_DEC 0x02 +#define EVENT_VALUE_PWM_FADE_MIN 0x03 +#define EVENT_VALUE_PWM_FADE_MAX 0x04 +#define EVENT_VALUE_PWM_TOGGLE 0x05 + +/* *********************************************************************** */ + +void pwm_init (void); +void pwm_event_handler (event_entry_t * p_event); +uint8_t pwm_get_sleep_mode (void); + +/* *********************************************************************** */ + +#endif /* __PWM_H__ */ diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..ba27339 --- /dev/null +++ b/timer.c @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 01/2019 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License, * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include "event.h" +#include "timer.h" + +/* *********************************************************************** */ + +#define TIMER_TIM_INIT() { \ + TCCR0A = 0x00; \ + TCCR0B = 0x00; \ + TIMSK0 = (1< 0) + { + timers[i]--; + if (timers[i] == 0) + { + event_queue(EVENT_TYPE_TIMER_ELAPSED, i, 0); + } + else + { + timer_needed = 1; + } + } + } + + /* stop timer */ + if (timer_needed == 0) + { + TIMER_TIM_DISABLE(); + } + + return 1; + } + + return 0; +} /* timer_check */ + + +void timer_event_handler(event_entry_t * p_event) +{ + if ((p_event->type == EVENT_TYPE_TIMER_SET) && + (p_event->num < TIMER_COUNT) + ) + { + timers[p_event->num] = p_event->value; + + /* start timer if needed */ + if (!TIMER_TIM_RUNNING()) + { + TIMER_TIM_RELOAD(TIMER_MSEC2TICKS(TIMER_IRQFREQ_MS)); + TIMER_TIM_ENABLE(); + } + } +} /* timer_event_handler */ + + +uint8_t timer_get_sleep_mode(void) +{ + if (TIMER_TIM_RUNNING()) + { + return SLEEP_MODE_IDLE; + } + else + { + return SLEEP_MODE_PWR_DOWN; + } + } /* timer_get_sleep_mode */ + + +void timer_init(void) +{ + TIMER_TIM_INIT(); + TIMER_TIM_RELOAD(TIMER_MSEC2TICKS(TIMER_IRQFREQ_MS)); + TIMER_TIM_ENABLE(); +} /* timer_init */ diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..f75271e --- /dev/null +++ b/timer.h @@ -0,0 +1,25 @@ +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include +#include "event.h" + +/* *********************************************************************** */ + +#define TIMER_COUNT 1 + +#define EVENT_TYPE_TIMER_SET 0x01 +#define EVENT_TYPE_TIMER_ELAPSED 0x02 + +#define EVENT_NUM_TIMER_PWM 0 + +/* *********************************************************************** */ + +void timer_init (void); +uint8_t timer_check (uint8_t timer_needed); +void timer_event_handler (event_entry_t * p_event); +uint8_t timer_get_sleep_mode (void); + +/* *********************************************************************** */ + +#endif /* __TIMER_H__ */