467 lines
16 KiB
C
467 lines
16 KiB
C
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "avr/io.h"
|
|
#include "zh_avr_160x_i2c.h"
|
|
#include "zh_avr_encoder.h"
|
|
#include "zh_avr_ac_dimmer.h"
|
|
#include "ate0003.h"
|
|
|
|
// #define DEBUG
|
|
|
|
#define SYSTEM_SETUP_STACK 189
|
|
#define ENCODER_STACK 165
|
|
#define PCF8574_STACK 105
|
|
|
|
const char *component_cmm[] = {component_1_cmm, component_2_cmm, component_3_cmm, component_4_cmm, component_5_cmm};
|
|
const char *component_line_1[] = {component_1_line_1, component_2_line_1, component_3_line_1, component_4_line_1, component_5_line_1};
|
|
const char *component_line_2[] = {component_1_line_2, component_2_line_2, component_3_line_2, component_4_line_2, component_5_line_2};
|
|
const uint8_t component_channel[] = {0x0F, 0x04, 0x02, 0x08, 0x0C};
|
|
|
|
#ifdef DEBUG
|
|
#define BAUD_RATE 9600
|
|
#define BAUD_PRESCALE (F_CPU / 16 / BAUD_RATE - 1)
|
|
|
|
int usart(char byte, FILE *stream)
|
|
{
|
|
while ((UCSR0A & (1 << UDRE0)) == 0)
|
|
{
|
|
}
|
|
UDR0 = byte;
|
|
return 0;
|
|
}
|
|
FILE uart = FDEV_SETUP_STREAM(usart, NULL, _FDEV_SETUP_WRITE);
|
|
#endif
|
|
|
|
void system_setup_task(void *pvParameters);
|
|
void component_setup_function(uint8_t component);
|
|
void relay_on_function(uint8_t relay_number);
|
|
void relay_off_function(uint8_t relay_number);
|
|
|
|
TaskHandle_t system_setup_task_handle = {0};
|
|
|
|
zh_avr_pcf8574_handle_t button_handle = {0};
|
|
zh_avr_pcf8574_handle_t led1_handle = {0};
|
|
zh_avr_pcf8574_handle_t led2_handle = {0};
|
|
zh_avr_pcf8574_handle_t relay_handle = {0};
|
|
zh_avr_pcf8574_handle_t lcd_handle = {0};
|
|
|
|
zh_avr_encoder_handle_t power_encoder_handle = {0};
|
|
zh_avr_encoder_handle_t component_encoder_handle = {0};
|
|
|
|
volatile static bool is_work = false; // Work status.
|
|
volatile static bool is_dmm = false; // DMM using status.
|
|
volatile static bool is_fix = false; // FIX button status.
|
|
volatile static uint8_t permitted_channels = 0; // Permitted channels. Depends of the selected component.
|
|
volatile static uint8_t used_channels = 0; // Counter of currently enabled channels.
|
|
volatile static bool is_initialized = false; // Normal loading status.
|
|
|
|
volatile static bool is_num1_fixed = false; // NUM 1 button FIX status.
|
|
volatile static bool is_num2_fixed = false; // NUM 2 button FIX status.
|
|
volatile static bool is_num3_fixed = false; // NUM 3 button FIX status.
|
|
volatile static bool is_num4_fixed = false; // NUM 4 button FIX status.
|
|
|
|
int main(void)
|
|
{
|
|
#ifdef DEBUG
|
|
UBRR0H = (BAUD_PRESCALE >> 8);
|
|
UBRR0L = BAUD_PRESCALE;
|
|
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
|
|
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
|
|
stdout = &uart;
|
|
#endif
|
|
xTaskCreate(system_setup_task, "system_setup", SYSTEM_SETUP_STACK, NULL, tskIDLE_PRIORITY, &system_setup_task_handle);
|
|
vTaskStartScheduler();
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
|
|
{
|
|
printf("Task %s Stack Owerflow.\n", pcTaskName);
|
|
printf("Free Heap %d.\n", xPortGetFreeHeapSize());
|
|
}
|
|
#endif
|
|
|
|
void system_setup_task(void *pvParameters)
|
|
{
|
|
char text_buffer[LCD_TEXT_BUFFER];
|
|
zh_avr_i2c_master_init(false);
|
|
zh_avr_pcf8574_init_config_t pcf8574_init_config = ZH_AVR_PCF8574_INIT_CONFIG_DEFAULT();
|
|
pcf8574_init_config.stack_size = PCF8574_STACK;
|
|
//** LED 1 extender init. **/
|
|
pcf8574_init_config.i2c_address = LED1_I2C_ADDRESS;
|
|
zh_avr_pcf8574_init(&pcf8574_init_config, &led1_handle);
|
|
zh_avr_pcf8574_write(&led1_handle, 0xFF);
|
|
//** LED 2 extender init. **/
|
|
pcf8574_init_config.i2c_address = LED2_I2C_ADDRESS;
|
|
zh_avr_pcf8574_init(&pcf8574_init_config, &led2_handle);
|
|
zh_avr_pcf8574_write(&led2_handle, 0xFF);
|
|
//** RELAY extender init. **/
|
|
pcf8574_init_config.i2c_address = RELAY_I2C_ADDRESS;
|
|
zh_avr_pcf8574_init(&pcf8574_init_config, &relay_handle);
|
|
zh_avr_pcf8574_write(&relay_handle, 0x00);
|
|
//** LCD init. **/
|
|
pcf8574_init_config.i2c_address = LCD_I2C_ADDRESS;
|
|
zh_avr_pcf8574_init(&pcf8574_init_config, &lcd_handle);
|
|
zh_avr_160x_init(&lcd_handle, ZH_LCD_16X4);
|
|
//** BUTTON extender init. **/
|
|
pcf8574_init_config.p0_gpio_work_mode = true;
|
|
pcf8574_init_config.p1_gpio_work_mode = true;
|
|
pcf8574_init_config.p2_gpio_work_mode = true;
|
|
pcf8574_init_config.p3_gpio_work_mode = true;
|
|
pcf8574_init_config.p4_gpio_work_mode = true;
|
|
pcf8574_init_config.p5_gpio_work_mode = true;
|
|
pcf8574_init_config.interrupt_port = AVR_PORTD;
|
|
pcf8574_init_config.interrupt_gpio = PORTD2;
|
|
pcf8574_init_config.i2c_address = BUTTON_I2C_ADDRESS;
|
|
zh_avr_pcf8574_init(&pcf8574_init_config, &button_handle);
|
|
//** Loading. Just for fun. **/
|
|
zh_avr_160x_set_cursor(&lcd_handle, 0, 0);
|
|
strcpy_P(text_buffer, text_company);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 1, 0);
|
|
strcpy_P(text_buffer, text_model);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 2, 0);
|
|
strcpy_P(text_buffer, text_firmware);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
for (uint8_t i = 0; i <= 100; i += 10)
|
|
{
|
|
zh_avr_160x_print_progress_bar(&lcd_handle, 3, i);
|
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
|
}
|
|
//** LCD default text print. **/
|
|
zh_avr_160x_lcd_clear(&lcd_handle);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 0, 0);
|
|
strcpy_P(text_buffer, text_ac_power);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 0, 11);
|
|
zh_avr_160x_print_int(&lcd_handle, 100);
|
|
zh_avr_160x_print_char(&lcd_handle, "%");
|
|
zh_avr_160x_set_cursor(&lcd_handle, 1, 0);
|
|
strcpy_P(text_buffer, text_component);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
//**"ANY" component setup after loading. **/
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, DMM_LED_RED, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, FIX_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, DMM_RELAY, RELAY_ON);
|
|
component_setup_function(0);
|
|
//** Encoders init **/
|
|
zh_avr_encoder_init_config_t encoder_init_config = ZH_AVR_ENCODER_INIT_CONFIG_DEFAULT();
|
|
encoder_init_config.stack_size = ENCODER_STACK;
|
|
encoder_init_config.queue_size = 5;
|
|
encoder_init_config.gpio_port = AVR_PORTC;
|
|
encoder_init_config.a_gpio_number = PORTC0;
|
|
encoder_init_config.b_gpio_number = PORTC1;
|
|
encoder_init_config.encoder_min_value = 0;
|
|
encoder_init_config.encoder_max_value = 100;
|
|
encoder_init_config.encoder_step = 5;
|
|
encoder_init_config.encoder_number = POWER_ENCODER;
|
|
zh_avr_encoder_init(&encoder_init_config, &power_encoder_handle);
|
|
zh_avr_encoder_set(&power_encoder_handle, 100);
|
|
encoder_init_config.a_gpio_number = PORTC2;
|
|
encoder_init_config.b_gpio_number = PORTC3;
|
|
encoder_init_config.encoder_min_value = 0;
|
|
encoder_init_config.encoder_max_value = (sizeof(component_cmm) / sizeof(component_cmm[0])) - 1;
|
|
encoder_init_config.encoder_step = 1;
|
|
encoder_init_config.encoder_number = COMPONENT_ENCODER;
|
|
zh_avr_encoder_init(&encoder_init_config, &component_encoder_handle);
|
|
zh_avr_encoder_set(&component_encoder_handle, 0);
|
|
//** AC dimmer init **/
|
|
zh_avr_ac_dimmer_init_config_t ac_dimmer_init_config = ZH_AVR_AC_DIMMER_INIT_CONFIG_DEFAULT();
|
|
ac_dimmer_init_config.ac_dimmer_frequency = ZH_50HZ;
|
|
ac_dimmer_init_config.zero_cross_port = AVR_PORTD;
|
|
ac_dimmer_init_config.zero_cross_gpio = PORTD3;
|
|
ac_dimmer_init_config.triac_port = AVR_PORTD;
|
|
ac_dimmer_init_config.triac_gpio = PORTD4;
|
|
zh_avr_ac_dimmer_init(&ac_dimmer_init_config);
|
|
zh_avr_ac_dimmer_set(100);
|
|
is_initialized = true;
|
|
#ifdef DEBUG
|
|
printf("%d.\n", uxTaskGetStackHighWaterMark(NULL));
|
|
#endif
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
void zh_avr_pcf8574_event_handler(zh_avr_pcf8574_event_on_isr_t *event)
|
|
{
|
|
switch (event->gpio_number)
|
|
{
|
|
case DMM_BUTTON:
|
|
if (event->gpio_level == LOW && is_work == false)
|
|
{
|
|
if (is_dmm == true)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, DMM_LED_RED, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, DMM_LED_GREEN, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, DMM_RELAY, RELAY_ON);
|
|
is_dmm = false;
|
|
}
|
|
else
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, DMM_LED_RED, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, DMM_LED_GREEN, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, DMM_RELAY, RELAY_OFF);
|
|
is_dmm = true;
|
|
}
|
|
}
|
|
break;
|
|
case FIX_BUTTON:
|
|
if (event->gpio_level == LOW)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, FIX_LED_BLUE, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, FIX_LED_GREEN, LED_ON);
|
|
is_fix = true;
|
|
}
|
|
else
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, FIX_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, FIX_LED_GREEN, LED_OFF);
|
|
is_fix = false;
|
|
}
|
|
break;
|
|
case NUM1_BUTTON:
|
|
if ((permitted_channels & CHANNEL1) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (event->gpio_level == LOW)
|
|
{
|
|
if (is_num1_fixed == true)
|
|
{
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM1_LED_BLUE, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM1_LED_GREEN, LED_ON);
|
|
relay_on_function(L1_RELAY);
|
|
}
|
|
else
|
|
{
|
|
if (is_fix == true)
|
|
{
|
|
is_num1_fixed = true;
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM1_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM1_LED_GREEN, LED_OFF);
|
|
relay_off_function(L1_RELAY);
|
|
is_num1_fixed = false;
|
|
}
|
|
break;
|
|
case NUM2_BUTTON:
|
|
if ((permitted_channels & CHANNEL2) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (event->gpio_level == LOW)
|
|
{
|
|
if (is_num2_fixed == true)
|
|
{
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM2_LED_BLUE, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM2_LED_GREEN, LED_ON);
|
|
relay_on_function(L2_RELAY);
|
|
}
|
|
else
|
|
{
|
|
if (is_fix == true)
|
|
{
|
|
is_num2_fixed = true;
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM2_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM2_LED_GREEN, LED_OFF);
|
|
relay_off_function(L2_RELAY);
|
|
is_num2_fixed = false;
|
|
}
|
|
break;
|
|
case NUM3_BUTTON:
|
|
if ((permitted_channels & CHANNEL3) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (event->gpio_level == LOW)
|
|
{
|
|
if (is_num3_fixed == true)
|
|
{
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM3_LED_BLUE, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM3_LED_GREEN, LED_ON);
|
|
relay_on_function(L3_RELAY);
|
|
}
|
|
else
|
|
{
|
|
if (is_fix == true)
|
|
{
|
|
is_num3_fixed = true;
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM3_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM3_LED_GREEN, LED_OFF);
|
|
relay_off_function(L3_RELAY);
|
|
is_num3_fixed = false;
|
|
}
|
|
break;
|
|
case NUM4_BUTTON:
|
|
if ((permitted_channels & CHANNEL4) == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (event->gpio_level == LOW)
|
|
{
|
|
if (is_num4_fixed == true)
|
|
{
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM4_LED_BLUE, LED_OFF);
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM4_LED_GREEN, LED_ON);
|
|
relay_on_function(L4_RELAY);
|
|
}
|
|
else
|
|
{
|
|
if (is_fix == true)
|
|
{
|
|
is_num4_fixed = true;
|
|
break;
|
|
}
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM4_LED_BLUE, LED_ON);
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM4_LED_GREEN, LED_OFF);
|
|
relay_off_function(L4_RELAY);
|
|
is_num4_fixed = false;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef DEBUG
|
|
printf("%d.\n", uxTaskGetStackHighWaterMark(NULL));
|
|
#endif
|
|
}
|
|
|
|
void zh_avr_encoder_event_handler(zh_avr_encoder_event_on_isr_t *event)
|
|
{
|
|
switch (event->encoder_number)
|
|
{
|
|
case POWER_ENCODER:
|
|
zh_avr_160x_set_cursor(&lcd_handle, 0, 11);
|
|
zh_avr_160x_print_int(&lcd_handle, (uint8_t)event->encoder_position);
|
|
zh_avr_160x_print_char(&lcd_handle, "% ");
|
|
zh_avr_ac_dimmer_set(event->encoder_position);
|
|
break;
|
|
case COMPONENT_ENCODER:
|
|
component_setup_function((uint8_t)event->encoder_position);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef DEBUG
|
|
printf("%d.\n", uxTaskGetStackHighWaterMark(NULL));
|
|
#endif
|
|
}
|
|
|
|
ISR(PCINT1_vect)
|
|
{
|
|
if (is_initialized == false)
|
|
{
|
|
return;
|
|
}
|
|
if (zh_avr_encoder_isr_handler(&power_encoder_handle) == pdTRUE)
|
|
{
|
|
portYIELD();
|
|
}
|
|
if (is_work == false)
|
|
{
|
|
if (zh_avr_encoder_isr_handler(&component_encoder_handle) == pdTRUE)
|
|
{
|
|
portYIELD();
|
|
}
|
|
}
|
|
}
|
|
|
|
ISR(PCINT2_vect)
|
|
{
|
|
if (is_initialized == false)
|
|
{
|
|
return;
|
|
}
|
|
zh_avr_ac_dimmer_isr_handler();
|
|
if (zh_avr_pcf8574_isr_handler() == pdTRUE)
|
|
{
|
|
portYIELD();
|
|
}
|
|
}
|
|
|
|
void component_setup_function(uint8_t component)
|
|
{
|
|
char text_buffer[LCD_TEXT_BUFFER];
|
|
permitted_channels = component_channel[component];
|
|
zh_avr_160x_set_cursor(&lcd_handle, 1, 10);
|
|
strcpy_P(text_buffer, component_cmm[component]);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 2, 0);
|
|
strcpy_P(text_buffer, component_line_1[component]);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_160x_set_cursor(&lcd_handle, 3, 0);
|
|
strcpy_P(text_buffer, component_line_2[component]);
|
|
zh_avr_160x_print_char(&lcd_handle, text_buffer);
|
|
zh_avr_pcf8574_write(&led1_handle, is_dmm == true ? 0xF9 : 0xFA);
|
|
zh_avr_pcf8574_write(&led2_handle, 0x0F);
|
|
if ((permitted_channels & CHANNEL1) == CHANNEL1)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM1_LED_BLUE, LED_ON);
|
|
}
|
|
if ((permitted_channels & CHANNEL2) == CHANNEL2)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led1_handle, NUM2_LED_BLUE, LED_ON);
|
|
}
|
|
if ((permitted_channels & CHANNEL3) == CHANNEL3)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM3_LED_BLUE, LED_ON);
|
|
}
|
|
if ((permitted_channels & CHANNEL4) == CHANNEL4)
|
|
{
|
|
zh_avr_pcf8574_write_gpio(&led2_handle, NUM4_LED_BLUE, LED_ON);
|
|
}
|
|
}
|
|
|
|
void relay_on_function(uint8_t relay_number)
|
|
{
|
|
if (++used_channels > 1) // If any other channel already work.
|
|
{
|
|
zh_avr_ac_dimmer_stop();
|
|
vTaskDelay(1);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, relay_number, RELAY_ON);
|
|
vTaskDelay(10);
|
|
zh_avr_ac_dimmer_start();
|
|
}
|
|
else
|
|
{
|
|
is_work = true;
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, relay_number, RELAY_ON);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, GROUND_RELAY, RELAY_ON);
|
|
vTaskDelay(10);
|
|
zh_avr_ac_dimmer_start();
|
|
}
|
|
}
|
|
|
|
void relay_off_function(uint8_t relay_number)
|
|
{
|
|
if (--used_channels > 0) // If any other channel already work.
|
|
{
|
|
zh_avr_ac_dimmer_stop();
|
|
vTaskDelay(1);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, relay_number, RELAY_OFF);
|
|
vTaskDelay(10);
|
|
zh_avr_ac_dimmer_start();
|
|
}
|
|
else
|
|
{
|
|
zh_avr_ac_dimmer_stop();
|
|
vTaskDelay(1);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, relay_number, RELAY_OFF);
|
|
zh_avr_pcf8574_write_gpio(&relay_handle, GROUND_RELAY, RELAY_OFF);
|
|
is_work = false;
|
|
}
|
|
} |