/** * main.c * * Main application entry point for Bigscreen Beyond application. * * Copyright (c) 2022 Bigscreen, Inc. */ #include #include #include #include "flash_mutex.h" #include "conf_board.h" #define I2C_DESIRED_SPEED (100000UL) #define TASK_USBLOAD_STACK_SIZE (1024/sizeof(portSTACK_TYPE)) #define TASK_USBLOAD_PRIORITY (configMAX_PRIORITIES - 1) extern void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed char *pcTaskName); extern void vApplicationIdleHook(void); extern void vApplicationTickHook(void); extern void vApplicationMallocFailedHook(void); extern void xPortSysTickHandler(void); void main_audio_callback(void); board_ver_t main_boardversion = board_ver_unknown; TaskHandle_t xUSBLoadTaskHandle = NULL; UBaseType_t task_watermarks[16]; I2C_Handle hi2c_1, hi2c_4; // Pin change interrupt for HPD static void hpd_irq(uint32_t source_id, uint32_t source_mask); // Pin change interrupt for GPIO4 and GPIO7 from VXR7200 static void vxr_gpio_irq(uint32_t source_id, uint32_t source_mask); /** * \brief Called if stack overflow during execution */ extern void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed char *pcTaskName) { printf("stack overflow %x %s\r\n", (unsigned int)pxTask, (portCHAR *)pcTaskName); /* If the parameters have been corrupted then inspect pxCurrentTCB to * identify which task has overflowed its stack. */ crashdump_set_crash_reason(CRASH_STACK_OVERFLOW); crashdump_set_crashed_task(pxTask, pcTaskName); crashdump_set_irqn(__get_IPSR()); crashdump_trigger(); // Shouldn't get here. for (;;) { } } /** * \brief This function is called by FreeRTOS idle task */ extern void vApplicationIdleHook(void) { static TickType_t previous_tick = 0; TickType_t current_tick = xTaskGetTickCount(); if((current_tick - previous_tick) >= 100) { previous_tick = current_tick; task_watermarks[taskid_USBLoad] = uxTaskGetStackHighWaterMark(xUSBLoadTaskHandle); task_watermarks[taskid_HID_Command] = uxTaskGetStackHighWaterMark(xTaskGetHandle("HID Command")); task_watermarks[taskid_HID_Report] = uxTaskGetStackHighWaterMark(xTaskGetHandle("HID Report")); task_watermarks[taskid_I2C] = uxTaskGetStackHighWaterMark(xTaskGetHandle("I2C")); task_watermarks[taskid_TIMED_START] = uxTaskGetStackHighWaterMark(xTaskGetHandle("TIMED_START")); task_watermarks[taskid_ADC] = uxTaskGetStackHighWaterMark(xTaskGetHandle("ADC")); task_watermarks[taskid_LED] = uxTaskGetStackHighWaterMark(xTaskGetHandle("LED")); task_watermarks[taskid_FAN] = uxTaskGetStackHighWaterMark(xTaskGetHandle("FAN")); task_watermarks[taskid_video_proc] = uxTaskGetStackHighWaterMark(xTaskGetHandle("video_proc")); task_watermarks[taskid_USART] = uxTaskGetStackHighWaterMark(xTaskGetHandle("USART")); task_watermarks[taskid_IDLE] = uxTaskGetStackHighWaterMark(xTaskGetHandle("IDLE")); task_watermarks[taskid_Tmr_Svc] = uxTaskGetStackHighWaterMark(xTaskGetHandle("Tmr Svc")); } } /** * \brief This function is called by FreeRTOS each tick */ extern void vApplicationTickHook(void) { } extern void vApplicationMallocFailedHook(void) { /* Called if a call to pvPortMalloc() fails because there is insufficient free memory available in the FreeRTOS heap. pvPortMalloc() is called internally by FreeRTOS API functions that create tasks, queues, software timers, and semaphores. The size of the FreeRTOS heap is set by the configTOTAL_HEAP_SIZE configuration constant in FreeRTOSConfig.h. */ /* Force an assert. */ configASSERT( ( volatile void * ) NULL ); } /** * \brief Configure the console UART. */ static void configure_console(void) { const usart_serial_options_t uart_serial_options = { .baudrate = CONF_UART_BAUDRATE, #if (defined CONF_UART_CHAR_LENGTH) .charlength = CONF_UART_CHAR_LENGTH, #endif .paritytype = CONF_UART_PARITY, #if (defined CONF_UART_STOP_BITS) .stopbits = CONF_UART_STOP_BITS, #endif }; #if defined (CONF_BOARD_UART_CONSOLE) /* Configure console UART. */ stdio_serial_init(CONF_UART, &uart_serial_options); #endif /* Specify that stdout should not be buffered. */ #if defined(__GNUC__) setbuf(stdout, NULL); #else /* Already the case in IAR's Normal DLIB default configuration: printf() * emits one character at a time. */ #endif } /** * \brief FreeRTOS Real Time Kernel example entry point. * * \return Unused (ANSI-C compatibility). */ int main(void) { // Initialize the SAM system and board board_init(); // Set 32kHz in bypass #if BOARD == USER_EXT_BOARD SUPC->SUPC_MR |= SUPC_MR_KEY_PASSWD | SUPC_MR_OSCBYPASS; #endif sysclk_init(); // Enable Real-Time Timer as a free running counter rtt_enable(); // Create a mutex for Flash access Init_Flash_Mutex(); // Initialize the console uart configure_console(); // Initialize PDM microphones init_pdm(); set_pdm_buffers(pdmmic_get_audio_left_buffer(), pdmmic_get_audio_right_buffer(), PCM_BUF_SIZE); start_pdm_transfer(); // Start I2C module for communication with DDIC #if BOARD == USER_EXT_BOARD hi2c_1.flexcom = DDIC_I2C_FLEXCOM; hi2c_1.SCL_Pin = PIN_DDIC_I2C_SCL; hi2c_1.SDA_Pin = PIN_DDIC_I2C_SDA; // I2C pins are initialized already in displayboard_init.c init_i2c(&hi2c_1, I2C_DESIRED_SPEED); #else hi2c_1.flexcom = FLEXCOM3; hi2c_1.SCL_Pin = PIO_PA4_IDX; hi2c_1.SDA_Pin = PIO_PA3_IDX; // Init i2c pins sysclk_enable_peripheral_clock(ID_PIOA); pio_set_peripheral(PIOA, PIO_PERIPH_A, (1 << 4)); pio_set_peripheral(PIOA, PIO_PERIPH_A, (1 << 3)); init_i2c(&hi2c_1, I2C_DESIRED_SPEED); #endif // Start I2C module for other processes hi2c_4.flexcom = HUB_I2C_FLEXCOM; hi2c_4.SCL_Pin = PIN_HUB_I2C_SCL; hi2c_4.SDA_Pin = PIN_HUB_I2C_SDA; // I2C pins are initialized already in displayboard_init.c init_i2c(&hi2c_4, I2C_DESIRED_SPEED); // Initialize USB Device driver udc_start(); vbus_monitor_start(); // Initialize adc and temperature sensor init_adc(); // Set the audio class callback for loading data udi_audio_register_callback(main_audio_callback); // Output demo information. printf("-- Bigscreen Beyond --\n"); printf("-- %s --\n", BOARD_NAME); printf("-- Version %s --\n", VERSION_STRING); printf("-- Compiled: %s %s --\n", __DATE__, __TIME__); if(xTaskCreate(task_usbload, "USBLoad", TASK_USBLOAD_STACK_SIZE, NULL, TASK_USBLOAD_PRIORITY, NULL) != pdPASS) { printf("Failed to create USB Loader task\r\n"); } xUSBLoadTaskHandle = xTaskGetHandle("USBLoad"); start_usbhid_task(); start_test_tasks(); start_video_task(); start_tundra_uart_task(); usart_interface_init(); // Enable HPD IRQ pio_handler_set_pin(PIN_DDIC_HPD, 0, hpd_irq); pio_handler_set_priority(DDIC_LOGIC_PIO, PIOA_IRQn, 3); // Enable this interrupt after we have finished applying startup settings // Now done in "test_and_debug.c" //pio_enable_interrupt(DDIC_LOGIC_PIO, pio_get_pin_group_mask(PIN_DDIC_HPD)); // Enable GPIO4 IRQ (TX1 reset from VXR7200) pio_handler_set_pin(PIN_DDIC_GPIO4, 0, vxr_gpio_irq); pio_handler_set_priority(DDIC_GPIO4_PIO, PIOB_IRQn, 3); pio_enable_interrupt(DDIC_GPIO4_PIO, pio_get_pin_group_mask(PIN_DDIC_GPIO4)); // Enable GPIO7 IRQ (TX0 reset from VXR7200) pio_handler_set_pin(PIN_DDIC_GPIO7, 0, vxr_gpio_irq); pio_handler_set_priority(DDIC_LOGIC_PIO, PIOA_IRQn, 3); pio_enable_interrupt(DDIC_LOGIC_PIO, pio_get_pin_group_mask(PIN_DDIC_GPIO7)); // Enable the crash handler IRQ enable_crashdump(); // Start the scheduler. vTaskStartScheduler(); // Will only get here if there was insufficient memory to create the idle task. return 0; } void main_system_shutdown(void) { const RGB_Color_t shutdown_color = { .r = 0, .g = 0, .b = 0 }; // Disables all enabled functional blocks and interrupts, hard shuts down the RTOS. // This function should only be used when attempting to jump to the bootloader // by keeping it safe from interruptions. Power_Safe_Shutdown(); // Need interrupts working here since we use RTOS for timing. rgb_led_set_color(MCU_I2C(), &shutdown_color); // Same here, i2c uses RTOS. vTaskSuspendAll(); // Disable all RTOS context switches. This task will continue running. // Turn off Fan and RGB LED set_fan_pwm(0); __disable_irq(); // Now make sure we are not interrupted in this function. disable_i2c(DDIC_I2C()); disable_i2c(MCU_I2C()); disable_fan(); udc_stop(); // exit USB and force a disconnect disable_pdm(); // Disable the RTOS. Need to stop any Systick SysTick->CTRL &= (~SysTick_CTRL_ENABLE_Msk); // The PendSV and SVCall interrupts could still trigger, // but they are all activated by Systick. If execution is // at this point then there can't be any pending interrupts // anyway. Interrupts were disabled a few lines up. If they // had been pending, they would have been serviced before // execution reached this point. } __always_inline I2C_Handle* DDIC_I2C(void) { return &hi2c_1; } __always_inline I2C_Handle* MCU_I2C(void) { return &hi2c_4; } void main_audio_enable(void) { pdmmic_audio_enable(); } void main_audio_disable(void) { pdmmic_audio_disable(); } void main_audio_callback(void) { // Simply unblock the USBload task BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; vTaskNotifyGiveFromISR(xUSBLoadTaskHandle, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } void vbus_monitor_start(void) { //ioport_set_pin_dir(USB_VBUS_PIN, IOPORT_DIR_INPUT); } bool vbus_is_present(void) { return true; //return ioport_get_pin_level(USB_VBUS_PIN); } // I2C handler #if BOARD == USER_EXT_BOARD void FLEXCOM1_Handler(void) { #else void FLEXCOM3_Handler(void) { #endif // Simply call the irq handler in the I2C driver i2c_irq_handler(&hi2c_1); } void FLEXCOM4_Handler(void) { i2c_irq_handler(&hi2c_4); } static void hpd_irq(uint32_t source_id, uint32_t source_mask) { (void)source_id; (void)source_mask; bool hpd_level = ioport_get_pin_level(PIN_DDIC_HPD); if(hpd_level) { // HPD is asserted, signal to the Linkbox linkbox_hpd_set_active(); } else { linkbox_hpd_set_inactive(); } } static volatile bool gpio4_status = false; static volatile bool gpio7_status = false; bool main_left_display_reset_status(void) { return gpio7_status; } bool main_right_display_reset_status(void) { return gpio4_status; } static void vxr_gpio_irq(uint32_t source_id, uint32_t source_mask) { (void)source_id; (void)source_mask; gpio4_status = ioport_get_pin_level(PIN_DDIC_GPIO4); gpio7_status = ioport_get_pin_level(PIN_DDIC_GPIO7); /* commenting this out for now, just release reset at boot if(gpio7_status) { // release reset as soon as the first display needs to turn on // if we wait for both TX0 and TX1 reset pins, we will miss TX0 initialization ioport_set_pin_level(PIN_OLED_RESX, true); } else { ioport_set_pin_level(PIN_OLED_RESX, false); } */ } /* Detect the board version BS1 or BS2 by checking which USB hub controller is on I2C. BS1 - Hubs are 2x USB3803 (addresses 0x10 and 0x12) BS2 - Hub is USB2517 (address 0x58) */ /* board_ver_t main_detect_board_version(void) { // Attempt to issue a software reset to USB3803. // Need to release USB Hub reset, wait for the bootup time (between 4-400ms), // and issue an I2C command //if(usb3803_check_if_present(MCU_I2C(), USB_HUB_1)){ // return board_ver_BS1; //} if(usb2517_check_if_present(MCU_I2C())){ return board_ver_BS2; } return board_ver_BS1; } */ board_ver_t get_mainboardver(void) { return main_boardversion; } void set_mainboardver(board_ver_t ver) { main_boardversion = ver; } /* void HardFault_Handler(void) { crashdump_set_crash_reason(CRASH_HARD_FAULT); crashdump_set_irqn(__get_IPSR()); crashdump_trigger(); // In case we get stuck repeatedly triggering a hard fault, we need to ensure that we // can return from this handler and keep executing. // So let's skip the program counter ahead past the faulting instruction. // Fun story, this function begins with a "push {r3, lr}" so we have to add 2 more // registers to find the stacked PC uint32_t* sptr = __get_MSP(); sptr += 2; // Back to original stack at exception entry (R0) sptr += 6; // R1, R2, R3, R12, LR, then finally PC *sptr += 2; // skip to next instruction. // if this was a 32-bit instruction, who cares. we'll skip it fully the next time in hard fault. //__asm("bkpt"); // Force a NRST pin reset, which completely resets the processor and all peripherals // uint32_t rstc_mode_reg; // rstc_mode_reg = RSTC->RSTC_MR; // rstc_mode_reg &= ~(RSTC_MR_ERSTL_Msk | RSTC_MR_KEY_Msk); // rstc_mode_reg |= RSTC_MR_ERSTL(0xFF); // longest possible reset time (about 2 seconds) // rstc_mode_reg |= RSTC_MR_ERSTL(0x07); // About 2^8 / 32768 seconds (7.8ms) // rstc_mode_reg |= RSTC_MR_KEY_PASSWD; // RSTC->RSTC_MR = rstc_mode_reg; // RSTC->RSTC_CR = RSTC_CR_KEY_PASSWD | RSTC_CR_EXTRST; // assert reset pin // We can't do an infinite loop here. It will never leave hard fault handler! //for(;;){ //} } */