/****************************************************************************** * Copyright (C) 2023 Advanced Micro Devices, Inc. All Rights Reserved. * SPDX-License-Identifier: MIT ******************************************************************************/ /* * helloworld.c: simple test application * * This application configures UART 16550 to baud rate 9600. * PS7 UART (Zynq) is not initialized by this application, since * bootrom/bsp configures it to baud rate 115200 * * ------------------------------------------------ * | UART TYPE BAUD RATE | * ------------------------------------------------ * uartns550 9600 * uartlite Configurable only in HW design * ps7_uart 115200 (configured by bootrom/bsp) */ #include #include "platform.h" #include "xil_printf.h" #include #include #include "registers.h" #include "spi.h" #include "buffered_peripheral.h" #include "spi_matrix.h" #include "xscugic.h" #include "xparameters.h" #include "xtmrctr.h" #include "xuartps.h" #define TIM0_INT_ID XPAR_FABRIC_TMRCTR_0_VEC_ID #define TMRCTR_DEVICE_ID XPAR_AXI_TIMER_0_DEVICE_ID #define TMRCTR_BASEADDRESS XPAR_AXI_TIMER_0_BASEADDR #define COMMAND_SEPARATOR '\n' static XScuGic IntcInstance; // Display matrix_t matrix; XTmrCtr timer; // SPI spi_t matrix_spi; buffered_transceiver_t matrix_tx; #define MAX_CMD_LENGTH 65 #define MAX_IMAGES 10 #define COLS 8 #define ROWS 8 uint32_t auto_toggle_cycles = 1000000; uint8_t images_count = 4; uint8_t current_image = 0; uint8_t replacing_image_index = 4; uint8_t images[MAX_IMAGES][8] = { {0b11111111, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b10000001, 0b11111111}, { 0b00000100, 0b00001110, 0b11110001, 0b10000101, 0b10000001, 0b10110001, 0b10110001, 0b11111111 }, { 0b11111000, 0b11100100, 0b11100010, 0b10000001, 0b10000001, 0b01000010, 0b00100100, 0b00011000 }, { 0b11000000, 0b10011000, 0b11010000, 0b10011010, 0b10010010, 0b00011010, 0b00000010, 0b00000011 }, }; uint32_t cycle = 0; bool auto_toggle = false; bool toggle_next = false; bool toggle_prev = false; bool animation = false; void tim_handler(void* callback_ref, uint8_t timer_ctr_number); void spi_handler(void); void num_to_string(uint16_t num, char* res, uint16_t digits) { for (uint16_t i = 0; i < digits; i++) { res[digits - 1 - i] = num % 10 + '0'; num /= 10; } } void handle_command(unsigned char* cmd, uint16_t len) { if (len == 0) { return; } bool handled = false; switch (cmd[0]) { case 'n': // next if (len == 1) { handled = true; toggle_next = true; printf("Switching to next image.\r\n"); } break; case 'p': // next if (len == 1) { handled = true; toggle_prev = true; printf("Switching to previous image.\r\n"); } break; case 'N': // auto toggle if (len == 1) { handled = true; auto_toggle = !auto_toggle; if (auto_toggle) { printf("Going to toggle automatically.\r\n"); } else { printf("Manual switch mode.\r\n"); } } break; case 'a': // animate if (len == 1) { handled = true; animation = !animation; if (animation) { printf("Animation enabled.\r\n"); } else { printf("Animation disabled.\r\n"); } } break; case 'u': // upload if (len != ROWS * COLS + 1) { break; } // null row for (uint8_t row = 0; row < ROWS; row++) { images[replacing_image_index][row] = 0; } // write data for (uint16_t i = 0; i < ROWS * COLS; i++) { uint8_t row = i >> 3; uint8_t col = i & 7; images[replacing_image_index][row] |= (cmd[1 + i] == 'X' ? 1 : 0) << col; } if (images_count < MAX_IMAGES) { images_count++; printf("Added image.\r\n"); } else { printf("Replaced image.\r\n"); } replacing_image_index++; replacing_image_index %= MAX_IMAGES; handled = true; break; case 's': // speed decrease if (len == 1) { handled = true; auto_toggle_cycles += 100000; printf("Auto toggle speed decreased.\r\n"); } break; case 'S': // speed increase if (len == 1) { handled = true; if (auto_toggle_cycles > 100000) { auto_toggle_cycles -= 100000; } printf("Auto toggle speed increased.\r\n"); } break; case 'l': // number of slots is... if (len == 1) { handled = true; printf("Used slots: "); char str[5] = { '\0', '\0', '\r', '\n', '\0' }; num_to_string(images_count, str, 2); printf(str); } break; case 'I': // increase intensity if (len == 1) { matrix_increase_intensity(&matrix); printf("Increasing intensity.\r\n"); handled = true; } break; case 'i': // decrease intensity if (len == 1) { matrix_decrease_intensity(&matrix); printf("Decreasing intensity.\r\n"); handled = true; } break; case 'h': printf("Help:\r\n"); printf(" h - show this help messages\r\n"); printf(" l - print used slots\r\n"); printf(" a - toggle animating\r\n"); printf(" n, p - next, prev image\r\n"); printf(" s, S - decrease, increase toggle speed\r\n"); printf(" N - automatically switch images\r\n"); printf(" I, i - increase, decrease intensity\r\n"); printf(" uIMG - upload new image, IMG has \"X\" for 1s and \"-\" for 0s. It should be 64 chars long\r\n"); handled = true; break; default: break; } if (!handled) { printf("Unknown command!\r\n"); } } unsigned char* receive_command(uint16_t* length) { static unsigned char cmd[MAX_CMD_LENGTH]; static uint16_t index; unsigned char* current_char = (cmd + index); while (XUartPs_Recv(XPAR_XUARTPS_0_DEVICE_ID, current_char, 1) == 1) { // echo printf("%c", *current_char); if (*current_char == COMMAND_SEPARATOR) { printf("\r"); *current_char = '\0'; *length = index; index = 0; return cmd; } index++; } // Commands are separated either through max length, or by new line if (index == MAX_CMD_LENGTH + 1) { *length = MAX_CMD_LENGTH; index = 0; return cmd; } return NULL; } void next_image(int8_t offset) { if (matrix.state != MATRIX_STABLE) { return; } current_image += offset; if (current_image > images_count) { current_image = images_count - 1; } current_image %= images_count; matrix_set_buffer(&matrix, MATRIX_SLOT_OTHER, &images[current_image][0]); if (animation) { matrix_animate_swap(&matrix); } else { matrix_swap(&matrix); } } void app_loop() { while (1) { if (auto_toggle && cycle >= auto_toggle_cycles) { next_image(1); cycle = 0; } else if (toggle_next) { next_image(1); toggle_next = false; } else if (toggle_prev) { next_image(-1); toggle_prev = false; } uint16_t command_len; unsigned char* cmd = receive_command(&command_len); if (cmd != NULL) { handle_command(cmd, command_len); } cycle++; } } int init_interrupts() { XScuGic_Config *IntcConfig; int status; /* Initialize the exception table */ Xil_ExceptionInit(); /* Look up the interrupt configuration */ IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID); if (IntcConfig == NULL) { xil_printf("Failed to lookup SCUGIC config.\r\n"); return XST_FAILURE; } /* Initialize the interrupt controller driver so that it is ready to use */ status = XScuGic_CfgInitialize(&IntcInstance, IntcConfig, IntcConfig->CpuBaseAddress); if (status != XST_SUCCESS) { xil_printf("Failed to initialize SCUGIC.\r\n"); return XST_FAILURE; } /* * Connect the interrupt controller interrupt handler to the hardware interrupt * handling logic in the processor. */ Xil_ExceptionRegisterHandler( XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, &IntcInstance ); /* Register (connect) your custom ISR to this interrupt */ status = XScuGic_Connect( &IntcInstance, SPI0_INT_ID, (Xil_ExceptionHandler) spi_handler, NULL ); if (status != XST_SUCCESS) { xil_printf("Failed to connect ISR.\r\n"); return XST_FAILURE; } /* Register (connect) your custom ISR to this interrupt */ status = XScuGic_Connect( &IntcInstance, TIM0_INT_ID, (XInterruptHandler)XTmrCtr_InterruptHandler, (void*)&timer ); if (status != XST_SUCCESS) { xil_printf("Failed to connect ISR for timer.\r\n"); return XST_FAILURE; } /* Enable the interrupt in the GIC */ XScuGic_Enable(&IntcInstance, XIL_EXCEPTION_ID_INT); /* Enable interrupts in the Processor */ Xil_ExceptionEnable(); return XST_SUCCESS; } int main() { init_platform(); print("Hello World\n\r"); print("Successfully ran Hello World application"); init_interrupts(); { // SPI init spi_init(&matrix_spi, SPI0); spi_master_configure(&matrix_spi, false, false, SPI_MSB_FIRST, SPI_FRAME_16_BIT, true); spi_master_configure_speed(&matrix_spi, 4); spi_enable_interrupt(&matrix_spi, true, false); spi_master_enable(&matrix_spi, true); } { // Matrix, timer init matrix_init(&matrix, &matrix_tx, 5); matrix_setup(&matrix); matrix_enable(&matrix, true); matrix_set_buffer(&matrix, MATRIX_SLOT0, images[current_image]); int status; status = XTmrCtr_Initialize(&timer, TMRCTR_DEVICE_ID); if (status != XST_SUCCESS) { print("Could not initialize timer.\r\n"); } status = XTmrCtr_SelfTest(&timer, TMRCTR_DEVICE_ID); if (status != XST_SUCCESS) { print("Timer self-test failed.\r\n"); } XTmrCtr_SetHandler(&timer, tim_handler, &timer); XTmrCtr_SetOptions(&timer, TMRCTR_DEVICE_ID, XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION); XTmrCtr_SetResetValue(&timer, TMRCTR_DEVICE_ID, 100000); XTmrCtr_Start(&timer, TMRCTR_DEVICE_ID); } // Application app_loop(); cleanup_platform(); return 0; } void tim_handler(void* callback_ref, uint8_t timer_ctr_number) { /* XTmrCtr *instance = (XTmrCtr *)callback_ref; */ matrix_update(&matrix, &matrix_tx); } void spi_handler(void) { // using can receive as not busy interrupt if (spi_can_receive(&matrix_spi)) { uint16_t tmp; spi_receive(&matrix_spi, &tmp, 1); } if (spi_can_transmit(&matrix_spi)) { buffered_transceiver_trigger_transmit(&matrix_tx, 1); } } bool spi_generic_can_receive(void *peripheral) { return spi_can_receive((spi_t*)peripheral); } bool spi_generic_can_transmit(void *peripheral) { return spi_can_transmit((spi_t*)peripheral); } uint16_t spi_generic_receive(void *peripheral, void *buffer, uint16_t max_size) { return spi_receive((spi_t*)peripheral, buffer, max_size); } uint16_t spi_generic_transmit(void *peripheral, void *data, uint16_t size) { // enable the interrupt so that we can send the rest uint16_t ret = spi_transmit((spi_t*)peripheral, data, size); return ret; }