Exemple #1
0
/**
 * \brief Show explanation screen
 *
 * This function draws an explanation screen, and is run if the applications are
 * started without the jumpers described in the documentation attached.
 */
static void show_explain_splash(void)
{
    struct keyboard_event input;

    /* Clear screen */
    gfx_mono_draw_filled_rect(0, 0, 128, 32, GFX_PIXEL_CLR);

    gfx_mono_draw_string("Class B Demonstration", 1, 4, &sysfont);
    gfx_mono_draw_string("See Application note", 4, 12, &sysfont);
    gfx_mono_draw_string("AVR1610", 40, 20, &sysfont);

    /* Any key will exit the loop */
    while (true) {
        oven_wdt_periodic_reset();
        keyboard_get_key_state(&input);
        if (input.type == KEYBOARD_RELEASE) {
            break;
        }
    }

    /* Clear screen */
    gfx_mono_draw_filled_rect(0, 0, 128, 32, GFX_PIXEL_CLR);
    gfx_mono_draw_string("Attach jumpers betwe-", 0, 4, &sysfont);
    gfx_mono_draw_string("en ADC2-ADC4 on J2,", 0, 12, &sysfont);
    gfx_mono_draw_string("and RXD-SS on J1", 0, 20, &sysfont);

    /* Any key will exit the loop */
    while (true) {
        oven_wdt_periodic_reset();
    }
}
Exemple #2
0
/**
 * \brief Show button names on display
 *
 * This function shows the user what the different on-board buttons do. When
 * the user presses a button, this function exits and the application can
 * continue.
 */
static void show_button_splash(void)
{
    struct keyboard_event input;

    /* Clear screen */
    gfx_mono_draw_filled_rect(0, 0, 128, 32, GFX_PIXEL_CLR);

    gfx_mono_draw_filled_circle(6, 6, 3, GFX_PIXEL_SET, GFX_WHOLE);
    gfx_mono_draw_filled_circle(6, 24, 3, GFX_PIXEL_SET, GFX_WHOLE);
    gfx_mono_draw_filled_circle(122, 6, 3, GFX_PIXEL_SET, GFX_WHOLE);
    gfx_mono_draw_filled_circle(122, 24, 3, GFX_PIXEL_SET, GFX_WHOLE);

    gfx_mono_draw_string("Oven power/", 50, 0, &sysfont);
    gfx_mono_draw_string("Up", 94, 8, &sysfont);
    gfx_mono_draw_string("Down", 90, 22, &sysfont);
    gfx_mono_draw_string("Menu/", 12, 0, &sysfont);
    gfx_mono_draw_string("Back", 12, 8, &sysfont);
    gfx_mono_draw_string("Pot/enter", 12, 22, &sysfont);

    /* Any key will exit the loop */
    while (true) {
        oven_wdt_periodic_reset();
        keyboard_get_key_state(&input);
        if (input.type == KEYBOARD_RELEASE) {
            break;
        }
    }
}
Exemple #3
0
/**
 * \brief Simulate corruption of a part of Flash
 *
 * This function simulates corruption of the Flash by modifying one of the
 * strings in the error insertion menu. More specifically it replaces the
 * "-- Error insertion --" menu title with "Out of cheese!". If called again,
 * the original string is reverted.
 */
static void oven_classb_flash_corrupter(void)
{
	/* Flash page base address of the menu title string */
	flash_addr_t page_addr = ((uintptr_t)error_menu_title / FLASH_PAGE_SIZE)
			* FLASH_PAGE_SIZE;
	/* Address within the page */
	uintptr_t string_addr = (uintptr_t)error_menu_title % FLASH_PAGE_SIZE;
	/* New string that we will replace it with */
	const char newstring[] = "    Out of cheese!   ";
	const char oldstring[] = "-- Error insertion --";

	/* Load page with strings from flash into our buffer */
	nvm_flash_flush_buffer();
	for (uint16_t addr = 0; addr < FLASH_PAGE_SIZE / 2; addr++) {
		flash_scramble_buf[addr] = nvm_flash_read_word((addr * 2)
				+ page_addr);
		oven_wdt_periodic_reset();
	}

	/* Check which string is the current and change it accordingly */
	if (strcmp((char *)flash_scramble_buf + string_addr, oldstring) == 0) {
		strcpy((char *)flash_scramble_buf + string_addr, newstring);
	} else {
		strcpy((char *)flash_scramble_buf + string_addr, oldstring);
	}

	oven_wdt_periodic_reset();

	/* Fill page buffer with our buffered data, then write it to flash */
	for (uint16_t addr = 0; addr < FLASH_PAGE_SIZE / 2; addr++) {
		nvm_flash_load_word_to_buffer(addr * 2,
				flash_scramble_buf[addr]);
		oven_wdt_periodic_reset();
	}
	nvm_flash_atomic_write_app_page(page_addr);
}
Exemple #4
0
/**
 * \brief Show menu for error insertion and handle user's selection.
 *
 * This function changes device configurations and does some hacks to make
 * the device behave incorrectly.
 *
 * The menu entries are
 *   - Change clock frequency: Changes the peripheral clock divider, simulating
 *     that the clock system has malfunctioned. This should be detected by the
 *     Class B frequency consistency test.
 *
 *   - Mess with test timer: Changes how often periodic tests are performed,
 *     simulating an error with an interrupt timer. This should be detected by
 *     the Class B interrupt monitor.
 *
 *   - Change a Flash section: Changes the string for the menu title stored in
 *     program memory to "Out of cheese", simulating Flash memory corruption.
 *     This can be changed back by selecting the menu item again. This should be
 *     detected by the Class B Flash CRC test.
 *
 *   - Scramble SRAM section: Starts a continuous DMA transfer in the background
 *     to a memory location, simulating transient SRAM corruption. This should
 *     be detected by the periodic and power-on Class B SRAM test.
 *
 *   - Enter infinite loop: Simulates a runaway program counter by looping
 *     forever. This should be detected by the watchdog timer system which is
 *     tested on device power-up.
 *
 *   - Change ADC reference: Enables a callback function for the ADC, which will
 *     change the voltage reference after the next completed conversion. This
 *     will cause the analog IO test to fail when user turns up the power to the
 *     plate.
 */
void oven_classb_error_insertion(void)
{
	uint8_t menu_status;
	struct keyboard_event input;
	struct adc_channel_config adcch_conf;

	/* Initialize menu system */
	gfx_mono_menu_init(&error_menu);

	/* Wait for user to select something in the menu system */
	do {
		do {
			keyboard_get_key_state(&input);
			oven_wdt_periodic_reset();
			/* Wait for key release */
		} while (input.type != KEYBOARD_RELEASE);

		/* Send key to menu system */
		menu_status = gfx_mono_menu_process_key(&error_menu,
				input.keycode);

		oven_wdt_periodic_reset();
	} while (menu_status == GFX_MONO_MENU_EVENT_IDLE);

	/* Handle the user's selection */
	switch (menu_status) {
	case 0:
		/* Change cpu frequency by modifying the prescalers */
		sysclk_set_prescalers(CLK_PSADIV_4_gc, CLK_PSBCDIV_1_1_gc);
		break;

	case 1:
		/* Change timing of the periodic temperature tests */
		OVEN_PERIODIC_TEMPTEST_TC.CTRLA = TC_CLKSEL_DIV256_gc;
		break;

	case 2:
		/* Change flash section. */
		oven_classb_flash_corrupter();
		break;

	case 3:
		/* Disrupt SRAM by setting up the DMA to write to a location on
		 * the heap, triggered by the class B frequency monitor timer
		 */
		PR.PRGEN &= ~PR_DMA_bm;
		DMA.CTRL |= DMA_ENABLE_bm;
		DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_TCD1_CCA_gc;
		/* Address of Timer D1 CNT base is 0x0960. */
		DMA.CH0.SRCADDR0 = 0x60;
		DMA.CH0.SRCADDR1 = 0x09;
		DMA.CH0.SRCADDR2 = 0x00;

		DMA.CH0.DESTADDR0 = ((uint16_t)&variable_for_sram_error) & 0xFF;
		DMA.CH0.DESTADDR1 = (((uint16_t)&variable_for_sram_error) >> 8)
				& 0xFF;
		DMA.CH0.DESTADDR2 = 0x00;

		DMA.CH0.CTRLA = DMA_CH_BURSTLEN_2BYTE_gc | DMA_CH_REPEAT_bm
				| DMA_CH_ENABLE_bm;
		break;

	case 4:
		/* Enter infinite loop */
		while (1) {
		}
		break;

	case 5:
		/* Set up ADC channel interrupt */
		adc_set_callback(&ADCA, adc_foul_callback);
		adcch_read_configuration(&ADCA, ADC_CH0, &adcch_conf);
		adcch_enable_interrupt(&adcch_conf);
		adcch_write_configuration(&ADCA, ADC_CH0, &adcch_conf);

		adcch_read_configuration(&ADCA, ADC_CH2, &adcch_conf);
		adcch_enable_interrupt(&adcch_conf);
		adcch_write_configuration(&ADCA, ADC_CH2, &adcch_conf);
		break;

	case 6:
		/* Back */
		break;

	case GFX_MONO_MENU_EVENT_EXIT:
		/* Fall through to default */
	default:
		/* Nothing, go back. */
		break;
	}
}
Exemple #5
0
/**
 * \brief Main function.
 *
 * Initializes the board, displays splash screens, then launches application.
 *
 * If the application exits (fails), the error is written to display and an
 * infinite loop with watchdog resets is entered.
 */
int main(void)
{
    /* Holds state of the QTouch button */
    enum pot_t potstate      = POT_OFF;
    /* Last state of the QTouch button for change detection */
    enum pot_t last_potstate = POT_OFF;
    /* Holds user-selected power-setting */
    uint8_t wattage      = 0;
    /* Last power-setting for change detection */
    uint8_t last_wattage = 0;
    /* Whether the plate element is/should be actuated */
    bool power      = false;
    /* Last power setting for change detection */
    bool last_power = false;
    /* Last time a simulation step was executed */
    uint32_t last_sim_step;
    /* Last time a control step was executed */
    uint32_t last_ctl_step;

    /* The WDT was just reset by the WDT functional test*/
    classb_last_wdt_reset = rtc_get_time();

    last_sim_step = classb_last_wdt_reset;
    last_ctl_step = classb_last_wdt_reset;

    sysclk_init();
    board_init();
    pmic_init();
    gfx_mono_init();
    touch_init();
    main_init_rtc32();

    cpu_irq_enable();

    /* Enable display backlight */
    ioport_set_pin_level(LCD_BACKLIGHT_ENABLE_PIN,
                         LCD_BACKLIGHT_ENABLE_LEVEL);

    /* If an error was detected, skip directly to displaying it */
    if (classb_error) {
        goto display_error;
    }


    /* Check if required jumpers are mounted, show explanation if not */
    if (!main_check_jumpers()) {
        show_explain_splash();
    }

    /* Display a splash screen showing button functions */
    show_button_splash();

    /* Initialize the ADC, DAC and Timer/Counter modules that are used to
     * emulate real world objects.
     */
    main_init_adc_dac();
    main_init_tc();

    /* Initialize subsystems used for Class B testing */
    oven_classb_init_tests();

    /* Reset the simulation states */
    oven_plant_init();

    /* Clear screen */
    gfx_mono_draw_filled_rect(0, 0, 128, 32, GFX_PIXEL_CLR);

    /* Draw the initial axis system */
    oven_ui_draw_axis();

    /* Draw the user interface */
    oven_ui_update_graphics(potstate, wattage, power);

    /* Run simulation as long as no error is detected in class B tests */
    while (!classb_error) {
        uint32_t current_time;

        oven_wdt_periodic_reset();
        current_time = rtc_get_time();

        /* Add power on SW1 press, wrap at 20 */
        if (oven_ui_power_button_is_pressed()) {
            wattage = (wattage + 5) % 20;
        }

        /* Check QTouch sensor and map this to `potstate`. This is
         * needed both for simulation and for the control routine.
         */
        potstate = (!touch_key_is_pressed()) ? POT_ON : POT_OFF;

        /* Execute control routine periodically */
        if (current_time > (last_ctl_step + OVEN_CTL_STEP_TIME)) {
            ovenctl_execute_control_step(current_time, &wattage,
                                         &power, potstate);
            last_ctl_step = current_time;
        }

        /* Execute simulation step periodically */
        if (current_time > (last_sim_step + OVEN_SIM_STEP_TIME)) {
            main_execute_simulation_step(current_time, potstate);
            last_sim_step = current_time;
        }

        /* Update graphics on wattage, power or pot on/off change */
        if ((potstate != last_potstate) || (power != last_power)
                || (wattage != last_wattage)) {
            oven_ui_update_graphics(potstate, wattage, power);
        }

        /* Update variable states for change detection on next loop
         * iteration.
         */
        last_power    = power;
        last_wattage  = wattage;
        last_potstate = potstate;

        /* If back/menu button is pressed, pause simulation and test
         * timers, and show menu.
         */
        if (oven_ui_back_button_is_pressed()) {
            /* Disable interrupt monitoring, if enabled */
            classb_intmon_set_state(TEMP_SANITY_TEST, M_DISABLE);
            classb_intmon_set_state(PER_CLASSB_TESTS, M_DISABLE);

            OVEN_PERIODIC_TEMPTEST_TC.CTRLA &= ~TC1_CLKSEL_gm;
            OVEN_PERIODIC_CLASSB_TC.CTRLA &= ~TC0_CLKSEL_gm;

            /* Show the error insertion menu */
            oven_classb_error_insertion();

            /* Re-enable timers upon return */
            OVEN_PERIODIC_CLASSB_TC.CTRLA |= TC_CLKSEL_DIV1024_gc;
            /* If user did not induce an error in the temperature
             * test timing, re-enable it correctly.
             */
            if ((OVEN_PERIODIC_TEMPTEST_TC.CTRLA & TC1_CLKSEL_gm)
                    == TC_CLKSEL_OFF_gc) {
                OVEN_PERIODIC_TEMPTEST_TC.CTRLA
                |= TC_CLKSEL_DIV1024_gc;
            }

            /* Re-enable interrupt monitoring if the oven is
             * supposed to be on, i.e., they were enabled before.
             */
            if (wattage > 0) {
                classb_intmon_set_state(TEMP_SANITY_TEST,
                                        M_ENABLE);
                classb_intmon_set_state(PER_CLASSB_TESTS,
                                        M_ENABLE);
            }

            /* Reset UI */
            gfx_mono_draw_filled_rect(0, 0, 128, 32, GFX_PIXEL_CLR);
            oven_ui_draw_axis();
            oven_ui_update_graphics(potstate, wattage, power);
        }
    }

display_error:
    /* Show red status LED and write the error on display */
    oven_ui_set_status_leds(S_RED);
    oven_classb_display_error();

    /* Enter infinite loop of watchdog resets so user can read display */
    while (true) {
        oven_wdt_periodic_reset();
    }
}