/******************************************************************************
* 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 <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include <stdint.h>
#include <stdbool.h>
#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;
}