<file_sep>/*
    bspI2c.h

    Board support for controlling I2C interfaces on NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2018/12 <NAME> wrote/arranged it
*/

#ifndef ___BSPI2C_H
#define ___BSPI2C_H

#include "stm32l4xx_ll_i2c.h"

#define PJDF_I2C1 I2C1 // Address of I2C1 memory mapped register block

#define MAX_NBYTE_SIZE 0xFF //maximum byte that I2C Interface can take

void BspI2C1_init(void);
void BspI2c_WaitWithTimeoutReset(uint32_t (*IsActive)(I2C_TypeDef *I2Cx), uint32_t value);
uint8_t I2C_read_ack(I2C_TypeDef* I2Cx);
uint8_t I2C_read_nack(I2C_TypeDef* I2Cx);
void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint32_t direction, uint32_t request, uint8_t nBytes);
void I2C_stop(I2C_TypeDef* I2Cx);
void I2C_write(I2C_TypeDef* I2Cx, uint8_t data);


#endif
<file_sep>/*
    bspSpi.c

    Board support for controlling SPI interfaces on NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#include "bsp.h"

// BspSPI1Init
// Initializes the SPI1 memory mapped register block and enables it for use
// as a master SPI device.
void BspSPI1Init()
{
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);

  /* Configure SPI1 communication */
  LL_SPI_SetBaudRatePrescaler(SPI1, LL_SPI_BAUDRATEPRESCALER_DIV256);
  LL_SPI_SetTransferDirection(SPI1,LL_SPI_FULL_DUPLEX);
  LL_SPI_SetClockPhase(SPI1, LL_SPI_PHASE_1EDGE);
  LL_SPI_SetClockPolarity(SPI1, LL_SPI_POLARITY_LOW);
  LL_SPI_SetTransferBitOrder(SPI1, LL_SPI_MSB_FIRST);
  LL_SPI_SetDataWidth(SPI1, LL_SPI_DATAWIDTH_8BIT);
  LL_SPI_SetNSSMode(SPI1, LL_SPI_NSS_SOFT);
  LL_SPI_SetRxFIFOThreshold(SPI1, LL_SPI_RX_FIFO_TH_QUARTER);
  LL_SPI_SetMode(SPI1, LL_SPI_MODE_MASTER);

    LL_GPIO_InitTypeDef GPIO_InitStruct;
    
    /*-------- Configure SCK, MISO, MOSI --------*/
    GPIO_InitStruct.Pin = LL_GPIO_PIN_5 | LL_GPIO_PIN_6 | LL_GPIO_PIN_7;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
     
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    /*--------- Configure alternate GPIO functions to SPI1 ------*/
  LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_5, LL_GPIO_AF_5);
  LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_6, LL_GPIO_AF_5);
  LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_7, LL_GPIO_AF_5);

  LL_SPI_Enable(SPI1);
}


// SPI_SendBuffer
// Sends the given data to the given SPI device
void SPI_SendBuffer(SPI_TypeDef *spi, uint8_t *buffer, uint16_t bufLength)
{    
    for (int i = 0; i < bufLength; i++) {
        while(!LL_SPI_IsActiveFlag_TXE(spi)); 
        LL_SPI_TransmitData8(spi, buffer[i]);
        while(!LL_SPI_IsActiveFlag_RXNE(spi));
        LL_SPI_ReceiveData8(spi);
    }
 }

// SPI_GetBuffer
// Sends the given data to the given SPI device.
// buffer: on entry contains the command to retrieve data from the spi device.
//    On exit the buffer is OVERWRITTEN with the data output by the device
//    in response to the command.
void SPI_GetBuffer(SPI_TypeDef *spi, uint8_t *buffer, uint16_t bufLength)
{
    int iread = 0;

    for (int i = 0; i < bufLength; i++) {
        while(!LL_SPI_IsActiveFlag_TXE(spi)); 
        LL_SPI_TransmitData8(spi, buffer[i]);
        while(!LL_SPI_IsActiveFlag_RXNE(spi));
        buffer[iread++] =LL_SPI_ReceiveData8(spi);
    }
 }


// Set a value to control the data rate of the given SPI interface
void SPI_SetDataRate(SPI_TypeDef *spi, uint16_t value)
{
  LL_SPI_SetBaudRatePrescaler(spi, value);
}

<file_sep>/*
    mp3Util.h
    Some utility functions for controlling the MP3 decoder.

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#ifndef __MP3UTIL_H
#define __MP3UTIL_H

#include "SD.h"
#include "globals.h"
#include "events.h"
#include "bsp.h"
#include "print.h"

typedef enum {
    VOLUP = 0,
    VOLDOWN
} VolumeCounter;

PjdfErrCode Mp3GetRegister(HANDLE hMp3, INT8U *cmdInDataOut, INT32U bufLen);
void Mp3StreamInit(HANDLE hMp3);
//void Mp3Test(HANDLE hMp3);
//void Mp3Stream(HANDLE hMp3, INT8U *pBuf, INT32U bufLen);
//void Mp3StreamSDFile(HANDLE hMp3, char *pFilename);
void Mp3VolumeControl(HANDLE hMp3, VolumeCounter state);

void Mp3FetchFileNames();
//void Mp3FetchFileNames(char **list, int maxRow, int col, int *size);
void Mp3StreamCycle(HANDLE hMp3);

#endif<file_sep># mp3-player
A Basic MP3 Player for B-L475E-IOT01A platform using the Adafruit Music Maker MP3 Shield, and the Adafruit 2.8” TFT LCD shield. Below are the features of the MP3 Player - to be implemented in an incremental way: •Play and Pause •Play Progress •Forward and Rewind •Display song progress on the LCD display •A feature to change song •Read song files from SD card
<file_sep>/*
    bspUart.h

    Board support for controlling UART interfaces

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it for NUCLEO-F401RE MCU
    2020/8 <NAME> wrote/arranged it for STM32L475
*/

#ifndef __BSPUART_H
#define __BSPUART_H

#define USARTx_INSTANCE               USART1
#define USARTx_CLKSOURCE              LL_RCC_USART2_CLKSOURCE
#define USARTx_CLK_ENABLE()           LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1)
#define USARTx_CLK_SOURCE()           LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2)

#define USARTx_GPIO_CLK_ENABLE()      LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB)   /* Enable the peripheral clock of GPIOB */
#define USARTx_TX_PIN                 LL_GPIO_PIN_6
#define USARTx_TX_GPIO_PORT           GPIOB
#define USARTx_SET_TX_GPIO_AF()       LL_GPIO_SetAFPin_0_7(GPIOB, LL_GPIO_PIN_6, LL_GPIO_AF_7)
#define USARTx_RX_PIN                 LL_GPIO_PIN_7
#define USARTx_RX_GPIO_PORT           GPIOB
#define USARTx_SET_RX_GPIO_AF()       LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_7, LL_GPIO_AF_7)

// Application interface to hardware
void UartInit(uint32_t baud);
void PrintByte(char c);
char ReadByte();


#endif /* __BSPUART_H */<file_sep>/*
    bspLcd.h

    Board support for controlling the ILI9341 LCD on the Adafruit '2.8" TFT LCD w/Cap Touch' via NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#include "stm32l4xx.h"

#ifndef __BSPLCD_H
#define __BSPLCD_H

#define LCD_ILI9341_CS_GPIO               GPIOA
#define LCD_ILI9341_CS_GPIO_Pin           LL_GPIO_PIN_2

#define LCD_ILI9341_DC_GPIO               GPIOA
#define LCD_ILI9341_DC_GPIO_Pin           LL_GPIO_PIN_15


#define LCD_ILI9341_CS_ASSERT()        LL_GPIO_ResetOutputPin(LCD_ILI9341_CS_GPIO, LCD_ILI9341_CS_GPIO_Pin);
#define LCD_ILI9341_CS_DEASSERT()      LL_GPIO_SetOutputPin(LCD_ILI9341_CS_GPIO, LCD_ILI9341_CS_GPIO_Pin);

#define LCD_ILI9341_DC_LOW()        LL_GPIO_ResetOutputPin(LCD_ILI9341_DC_GPIO, LCD_ILI9341_DC_GPIO_Pin);
#define LCD_ILI9341_DC_HIGH()       LL_GPIO_SetOutputPin(LCD_ILI9341_DC_GPIO, LCD_ILI9341_DC_GPIO_Pin);

#define LCD_SPI_DEVICE_ID  PJDF_DEVICE_ID_SPI1

#define LCD_SPI_DATARATE  LL_SPI_BAUDRATEPRESCALER_DIV2  // Tune to find optimal value LCD controller will work with. OK with 16MHz and 80MHzHCLK

void BspLcdInitILI9341();



#endif<file_sep>/*
    uiSettings.h
    Header file for controlling objects on UI of LCD Display - carries display object coordinates

    Developed for University of Washington embedded systems programming certificate
    
    2021/3 <NAME> wrote/arranged it
*/

#ifndef __MP3UISETTINGS_H
#define __MP3UISETTINGS_H

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
/*
    Definition: MP3 Main Player Window and its labels UI Interface
    Note:       Contains all coordinate related Information/definitions
                Window space is divided into two halves - first half (240*160) music file related display & progress bar
                And second half is MP3 controller, that contains buttons
                (0, 0) - Top Left Coordinate

        -------------------------------------------------------------
        |       <.Song Title....................(Wrap Around)>      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |           Music BitMap File                |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       |                                            |      |
        |       ----------------------------------------------      |
        |-----------------------------------------------------------|
        |                   Progress Bar                            |
        |-----------------------------------------------------------|
        |   ----------------  ----------------  ----------------    |
        |   |              |  |              |  |              |    |
        |   |   Play       |  |    Pause     |  |    Stop      |    |
        |   |              |  |              |  |              |    |
        |   ----------------  ----------------  ----------------    |
        |             ---------------  --------------               |
        |             |             |  |            |               |
        |             |    Prev     |  |   Next     |               |
        |             |             |  |            |               |
        |             ---------------  --------------               |
        |                                            -------------  |
        |   ----------------                         |           |  |
        |   |              |                         |   Vol+    |  |
        |   |   Menu       |     <Play Status>       |           |  |
        |   |              |                         -------------  |
        |   ----------------                         -------------  |
        |                                            |           |  |
        |                                            |    Vol-   |  |
        |                                            |           |  |
        |                                            -------------  |
        --------------------------------------------------------------

Alternatively,
        -------------------------------------------------------------
        |       <.Song Title1....................(Wrap Around)>     |
        |       <.Song Title2....................(Wrap Around)>     |
        |       <.Song Title3....................(Wrap Around)>     |
        |       <.Song Title4....................(Wrap Around)>     |
        |       <.Song Title5....................(Wrap Around)>     |
        |       <.Song Title6....................(Wrap Around)>     |
        |       <.Song Title7....................(Wrap Around)>     |
        |       <.Song Title8....................(Wrap Around)>     |
        |       <.Song Title9....................(Wrap Around)>     |
        |       <.Song Title10...................(Wrap Around)>     |
        |-----------------------------------------------------------|
        |                   Progress Bar                            |
        |-----------------------------------------------------------|
        |   ----------------  ----------------  ----------------    |
        |   |              |  |              |  |              |    |
        |   |   Play       |  |    Pause     |  |    Stop      |    |
        |   |              |  |              |  |              |    |
        |   ----------------  ----------------  ----------------    |
        |             ---------------  --------------               |
        |             |             |  |            |               |
        |             |    Rewind   |  |     FF     |               |
        |             |             |  |            |               |
        |             ---------------  --------------               |
        |   -------------                            -------------  |
        |   |           |                            |           |  |
        |   |   UP      |                            |   Vol+    |  |
        |   |           |        <Play Status>       |           |  |
        |   -------------                            -------------  |
        |   -------------                            -------------  |
        |   |           |                            |           |  |
        |   |   DOWN    |                            |    Vol-   |  |
        |   |           |                            |           |  |
        |   -------------                            -------------  |
        --------------------------------------------------------------

*/

// Standard button width and height
#define STD_BUTTON_WIDTH   68U
#define STD_BUTTON_HEIGHT  30U

// Small button width and height
#define SM_BUTTON_WIDTH    60U
#define SM_BUTTON_HEIGHT   30U

// Menu button width and height
#define MENU_BUTTON_WIDTH  240U
#define MENU_BUTTON_HEIGHT 20U

// Status Bar button width and height
#define BAR_BUTTON_WIDTH   30U//18U
#define BAR_BUTTON_HEIGHT  18U

#define BAR_BUTTON_XCOORD  15U//14U
// Space between status bar
#define BAR_BUTTON_SPACE   24U // x-center of rectangle + 6 (distance between bars) 


// Volume Box and Stack button width and height
#define BOX_BUTTON_WIDTH   18U
#define BOX_BUTTON_HEIGHT  105U

#define STK_BUTTON_WIDTH   16U//18U
#define STK_BUTTON_HEIGHT  10U

#define STK_BUTTON_XCOORD  225U//14U

#define STK_BUTTON_SPACE   2U 

// Player Window Buttons (Designed on lower half) and other Objects Coordinate Mapping
#define MENU_XCOORD_BEGIN  120U
#define MENU_YCOORD_BEGIN  10U

#define PLAY_XCOORD      40U
#define PLAY_YCOORD      185U

#define PAUSE_XCOORD     120U
#define PAUSE_YCOORD     185U

#define STOP_XCOORD      200U
#define STOP_YCOORD      185U

#define REWIND_XCOORD    65U
#define REWIND_YCOORD    222U

#define FF_XCOORD        145U
#define FF_YCOORD        222U

//#define MENU_XCOORD    85U
//#define MENU_YCOORD    230U

#define UP_XCOORD        40U
#define UP_YCOORD        259U

#define DOWN_XCOORD      40U
#define DOWN_YCOORD      299U

#define VOLPLUS_XCOORD   180U
#define VOLPLUS_YCOORD   259U

#define VOLMINUS_XCOORD  180U
#define VOLMINUS_YCOORD  299U

#define STATUS_XCOORD    80U
#define STATUS_YCOORD    272U

#define VOLBOX_XCOORD    225U
#define VOLBOX_YCOORD    260U

#define PLAYERBUTTONNUM  9U
#define MAXMENULIST      7U
#define STATUSBARSNUM    10U
#define VOLUMEBARSNUM    14U

#define DEFAULTVOLINDX   5U

// MP3 Player Button Indexes
typedef enum
{
    PLAY = 0,
    PAUSE,
    STOP,
    REWIND,
    FF,
    UP,
    DOWN,
    VOLPLUS,
    VOLMINUS
} PlayerButtonIndex;

// Declare MP3 Player Winodows here

// The Player Window - where functions of controlling MP3 events are provided
typedef struct _PlayerWindow
{
    // -----------Start of Upper Half of the display--------------------//
    // Music Title & Music Bitmap File
    Adafruit_GFX_Button menu_list[MAXMENULIST];
    
    // Music Progress bar
    Adafruit_GFX_Button status_box;
    Adafruit_GFX_Button status_bar[STATUSBARSNUM];
    
    // -----------End of Upper Half of the display--------------------//
  
    // -----------Start of Lower Half of the display--------------------//
    // A list of MP3 Player buttons (Play, Pause, Stop, Prev, Next, Menu, Vol+, Vol-)
    Adafruit_GFX_Button button_list[PLAYERBUTTONNUM];
    
    // Current Play Status - Text Content
    char *player_status;
    
    // Volume Bar
    Adafruit_GFX_Button vol_box;
    Adafruit_GFX_Button vol_bar[VOLUMEBARSNUM];
    // -----------End of Lower Half of the display--------------------//
} PlayerWindow;

#endif<file_sep>/*
    bspLed.c

    Board support for controlling LEDs

    Developed for University of Washington embedded systems programming certificate
    
    2020/8 <NAME> wrote/arranged it for STM332L475
*/

#include "bsp.h"

void LedInit()
{
  /* Enable the LED2 Clock */
  LED2_GPIO_CLK_ENABLE();

  /* Configure IO in output push-pull mode to drive external LED2 */
  LL_GPIO_SetPinMode(LED2_GPIO_PORT, LED2_PIN, LL_GPIO_MODE_OUTPUT);
}

void SetLED(BOOLEAN On)
{
    if (On) {
        LL_GPIO_SetOutputPin(GPIO_PORT_LD2, GPIO_PIN_LD2);
      } else {
        /* The high 16 bits of BSRR reset the pin */
        LL_GPIO_ResetOutputPin(GPIO_PORT_LD2, GPIO_PIN_LD2);
     }
}
<file_sep>/*
    hw_init.c

    Initializes hardware for the app
       
    2021/1 <NAME> wrote/arranged it
*/
#include "bsp.h"

void SystemClock_Config16(void);
void SystemClock_Config80(void);


// Boot up processor
void Hw_init(void) {
  
    SystemClock_Config16();
    UartInit(115200);
    NVIC_SetPriority(PendSV_IRQn, 0xFF); // Lowest possible priority
}

void SetSysTick(uint32_t ticksPerSec)
{
    OS_CPU_SysTickInit(ticksPerSec);
    LL_RCC_ClocksTypeDef rcc_ClocksStatus;
    LL_RCC_GetSystemClocksFreq(&rcc_ClocksStatus);    
    PrintString("HCLK frequency = "); Print_uint32(rcc_ClocksStatus.HCLK_Frequency / 1000000); PrintString(" MHz\n");
    PrintString("Configured ticksPerSec = "); Print_uint32(ticksPerSec); PrintString("\n");
}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config16(void)
{
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
  while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_0)
  {
  }
  LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  LL_RCC_HSI_Enable();

   /* Wait till HSI is ready */
  while(LL_RCC_HSI_IsReady() != 1)
  {

  }
  LL_RCC_HSI_SetCalibTrimming(16);
  LL_RCC_MSI_Enable();

   /* Wait till MSI is ready */
  while(LL_RCC_MSI_IsReady() != 1)
  {

  }
  LL_RCC_MSI_EnablePLLMode();
  LL_RCC_MSI_EnableRangeSelection();
  LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
  LL_RCC_MSI_SetCalibTrimming(0);
  LL_PWR_EnableBkUpAccess();
//  LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_LOW);
//  LL_RCC_LSE_Enable();
//
//   /* Wait till LSE is ready */
//  while(LL_RCC_LSE_IsReady() != 1)
//  {
//
//  }
  LL_RCC_PLLSAI1_ConfigDomain_48M(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 24, LL_RCC_PLLSAI1Q_DIV_2);
  LL_RCC_PLLSAI1_EnableDomain_48M();
  LL_RCC_PLLSAI1_Enable();

   /* Wait till PLLSAI1 is ready */
  while(LL_RCC_PLLSAI1_IsReady() != 1)
  {

  }
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);

   /* Wait till System clock is ready */
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
  {

  }
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  LL_SetSystemCoreClock(16000000);

//   /* Update the time base */
//  if (HAL_InitTick (TICK_INT_PRIORITY) != HAL_OK)
//  {
//    Error_Handler();
//  }
//  LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
//  LL_RCC_SetUSARTClockSource(LL_RCC_USART3_CLKSOURCE_PCLK1);
//  LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
//  LL_RCC_SetI2CClockSource(LL_RCC_I2C2_CLKSOURCE_PCLK1);
//  LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1);
//  LL_RCC_SetDFSDMClockSource(LL_RCC_DFSDM_CLKSOURCE_PCLK);
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follows :
  *            System Clock source            = PLL (MSI)
  *            SYSCLK(Hz)                     = 80000000
  *            HCLK(Hz)                       = 80000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 1
  *            APB2 Prescaler                 = 1
  *            MSI Frequency(Hz)              = 4000000
  *            PLL_M                          = 1
  *            PLL_N                          = 40
  *            PLL_R                          = 2
  *            Flash Latency(WS)              = 4
  * @param  None
  * @retval None
  */
void SystemClock_Config80(void)
{
  /* MSI configuration and activation */
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_4);
  LL_RCC_MSI_Enable();
  while(LL_RCC_MSI_IsReady() != 1) 
  {
  };
  
  /* Main PLL configuration and activation */
  LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 40, LL_RCC_PLLR_DIV_2);
  LL_RCC_PLL_Enable();
  LL_RCC_PLL_EnableDomain_SYS();
  while(LL_RCC_PLL_IsReady() != 1) 
  {
  };
  
  /* Sysclk activation on the main PLL */
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) 
  {
  };
  
  /* Set APB1 & APB2 prescaler*/
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);

  /* Set systick to 1ms in using frequency set to 80MHz */
  /* This frequency can be calculated through LL RCC macro */
  /* ex: __LL_RCC_CALC_PLLCLK_FREQ(__LL_RCC_CALC_MSI_FREQ(LL_RCC_MSIRANGESEL_RUN, LL_RCC_MSIRANGE_6), 
                                  LL_RCC_PLLM_DIV_1, 40, LL_RCC_PLLR_DIV_2)*/
  //LL_Init1msTick(80000000);
  
  /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
  LL_SetSystemCoreClock(80000000);
}


<file_sep>/*
    mp3UserInterface.c
    MP3 Player User Interfacing implementation.
    This is the implementation of the mp3UserInterface.h driver interface exposed to applications.

    Developed for University of Washington embedded systems programming certificate
    
    2021/3 <NAME> wrote it
*/

#include "mp3UserInterface.h"

#define DEAFULT_VOL_POS   7U
// Button Intialization list
static ButtonParameter playerParamList[PLAYERBUTTONNUM] = 
{
    {"Play",  PLAY_XCOORD,    PLAY_YCOORD,     STD_BUTTON_WIDTH,  STD_BUTTON_HEIGHT },
    {"Pause", PAUSE_XCOORD,   PAUSE_YCOORD,    STD_BUTTON_WIDTH,  STD_BUTTON_HEIGHT },
    {"Stop",  STOP_XCOORD,    STOP_YCOORD,     STD_BUTTON_WIDTH,  STD_BUTTON_HEIGHT },
    {"Rewind",REWIND_XCOORD,  REWIND_YCOORD,   STD_BUTTON_WIDTH,  STD_BUTTON_HEIGHT },
    {"FF",    FF_XCOORD,      FF_YCOORD,       STD_BUTTON_WIDTH,  STD_BUTTON_HEIGHT },
    {"Up",    UP_XCOORD,      UP_YCOORD,       SM_BUTTON_WIDTH,   SM_BUTTON_HEIGHT},
    {"Down",  DOWN_XCOORD,    DOWN_YCOORD,     SM_BUTTON_WIDTH,   SM_BUTTON_HEIGHT},
    {"Vol+",  VOLPLUS_XCOORD, VOLPLUS_YCOORD,  SM_BUTTON_WIDTH,   SM_BUTTON_HEIGHT },
    {"Vol-",  VOLMINUS_XCOORD,VOLMINUS_YCOORD, SM_BUTTON_WIDTH,   SM_BUTTON_HEIGHT }
};

static char* player_status[]       = {"Select&Play",
                                      "Playing....",
                                      "Paused.....",
                                      "Stopped...."};

// Active Buttons Count
static INT8U activeMenuBtnCnt = 0;
static INT8U currMenuSelectCounter = 0;
// Adafruit LCD Controller Instance
Adafruit_ILI9341 lcdCtrl = Adafruit_ILI9341();
    
// Status Bar Count
static INT8S statusBarBtnCnt = -1;
static INT8S volumeBarBtnCnt = DEAFULT_VOL_POS;

// Private function definitions
static void buttonPressResponse(Adafruit_GFX_Button *button);
static void buttonReleaseResponse(Adafruit_GFX_Button *button);
static void setMenuToSelectState(Adafruit_GFX_Button *menu);
static void updateStatusBar(Adafruit_GFX_Button *status, BOOLEAN state);
static void updateVolumeBar(Adafruit_GFX_Button *vol, BOOLEAN state);
static void setMenuToInactiveState(Adafruit_GFX_Button *menu);
static INT8U getActiveButtonCount (boolean upDownFlag);
static void drawPlayStatus(char *status);
static void PrintCharToLcd(char c);

void InitMenuLabels(PlayerWindow *pWindow, boolean upDownFlag);

/*******************************************************************************
 * Function:  SetUpLCDInterface
 * 
 * Description: Set Up LCD Controller and related interface.
 * 
 * Arguments:   
 * 
 * Return Value: None
 *
 ******************************************************************************/
void SetUpLCDInterface (void)
{
    PjdfErrCode pjdfErr;
    INT32U length;
        
    // Open handle to the LCD driver
    HANDLE hLcd = Open(PJDF_DEVICE_ID_LCD_ILI9341, 0);
    //hang here for debugging purpose - to be replaced with Error log if project scope is expanded
    if (!PJDF_IS_VALID_HANDLE(hLcd)) while(1); 
    
    // Open handle to the SPI driver
    HANDLE hSPI = Open(LCD_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);
    
    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hLcd, PJDF_CTRL_LCD_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);
    
    // Now Initialize the LCD Controller
    lcdCtrl.setPjdfHandle(hLcd);
    lcdCtrl.begin();
}

/*******************************************************************************
 * Function:  InitPlayerWindow
 * 
 * Description: Intialize PlayerWindow Datastructure
 *              To be called at the beginning of display task
 * 
 * Arguments:    None
 * 
 * Return Value: None
 *
 ******************************************************************************/
 void InitPlayerWindow(PlayerWindow *pWindow)
 {
     OS_CPU_SR cpu_sr;
     
     OS_ENTER_CRITICAL(); 
     
     // allow slow lower pri drawing operation to finish without preemption
     // Clear Screen - And set up a background color for the screen
     lcdCtrl.fillScreen(ILI9341_WHITE);
     
     // Init List of menu
     for (int i = 0; i < MAXMENULIST; i++)
     {
         pWindow->menu_list[i] = Adafruit_GFX_Button();
         pWindow->menu_list[i].initButton(                       
                                   &lcdCtrl,                       
                                   MENU_XCOORD_BEGIN, 
                                   MENU_YCOORD_BEGIN + (i*MENU_BUTTON_HEIGHT),                       
                                   MENU_BUTTON_WIDTH, 
                                   MENU_BUTTON_HEIGHT,                      
                                   ILI9341_WHITE,                  // outline                       
                                   ILI9341_BLUE,                   // fill                       
                                   ILI9341_WHITE,                  // text color                       
                                   "",                       
                                   1);
     }
     
     // If there are active menu buttons, first item is by default selected
     activeMenuBtnCnt = getActiveButtonCount(true);
     
     InitMenuLabels(pWindow, true);
     
     if(activeMenuBtnCnt > 0)
     {
         setMenuToSelectState(&pWindow->menu_list[0]);
         pWindow->menu_list[0].drawButton(false, true);
     }
     
     // Initialize Status Box and Status Bar
     pWindow->status_box = Adafruit_GFX_Button();
     pWindow->status_box.initButton(                       
                                   &lcdCtrl,                       
                                   MENU_XCOORD_BEGIN, 
                                   MENU_YCOORD_BEGIN + (MAXMENULIST*MENU_BUTTON_HEIGHT)+3,                       
                                   MENU_BUTTON_WIDTH, 
                                   MENU_BUTTON_HEIGHT,                      
                                   ILI9341_BLACK,                                        
                                   ILI9341_WHITE,                                         
                                   ILI9341_WHITE,                                         
                                   "",                       
                                   1);
     pWindow->status_box.drawButton(false, false);

    for (int i = 0; i < STATUSBARSNUM; i++)
     {
         pWindow->status_bar[i] = Adafruit_GFX_Button();
         pWindow->status_bar[i].initButton(                       
                                   &lcdCtrl,                       
                                   BAR_BUTTON_XCOORD + (i * BAR_BUTTON_SPACE), 
                                   MENU_YCOORD_BEGIN + (MAXMENULIST*MENU_BUTTON_HEIGHT)+3,                       
                                   BAR_BUTTON_WIDTH, 
                                   BAR_BUTTON_HEIGHT,                      
                                   ILI9341_BLUE,                                       
                                   ILI9341_WHITE,                                          
                                   ILI9341_WHITE,                                       
                                   "",                       
                                   1);
         //pWindow->status_bar[i].drawButton(false, false);
     }
   
     // Initialize Status Box and Status Bar
     pWindow->vol_box = Adafruit_GFX_Button();
     pWindow->vol_box.initButton(                       
                                   &lcdCtrl,                       
                                   VOLBOX_XCOORD, 
                                   VOLBOX_YCOORD,                       
                                   BOX_BUTTON_WIDTH, 
                                   BOX_BUTTON_HEIGHT,                      
                                   ILI9341_BLACK,                                        
                                   ILI9341_WHITE,                                         
                                   ILI9341_WHITE,                                         
                                   "",                       
                                   1);
     pWindow->vol_box.drawButton(false, false);
     
     for (int i = 0; i < VOLUMEBARSNUM; i++)
     {
         pWindow->vol_bar[i] = Adafruit_GFX_Button();
         pWindow->vol_bar[i].initButton(                       
                                   &lcdCtrl,                       
                                   STK_BUTTON_XCOORD, 
                                   306 - (i * 7),                       
                                   STK_BUTTON_WIDTH, 
                                   STK_BUTTON_HEIGHT,                      
                                   ILI9341_WHITE,                                       
                                   ILI9341_BLUE,                                          
                                   ILI9341_WHITE,                                       
                                   "",                       
                                   1);
     }
    
    // display default volume psoition
    for (int i = 0; i <= volumeBarBtnCnt; i++)
    {
        pWindow->vol_bar[i].drawButton(false, false);
    }
    
     // Initialize a list of player buttons
     for (int i = 0; i < PLAYERBUTTONNUM; i++)
     {
         pWindow->button_list[i] = Adafruit_GFX_Button();
         pWindow->button_list[i].initButton(                       
                                   &lcdCtrl,                       
                                   playerParamList[i].x_coordinate, 
                                   playerParamList[i].y_coordinate,                       
                                   playerParamList[i].width, 
                                   playerParamList[i].height,                      
                                   ILI9341_BLACK,                      // outline                       
                                   ILI9341_WHITE,                      // fill                       
                                   ILI9341_BLACK,                      // text color                       
                                   playerParamList[i].label,                       
                                   1);
          pWindow->button_list[i].drawButton(false, false);
     }
     
     drawPlayStatus(player_status[SELECT]);
     
     OS_EXIT_CRITICAL();
 }
 
/*******************************************************************************
 * Function:  drawPlayerWindow
 * 
 * Description: Display the MP3 Player Window
 * 
 * Arguments:   None
 * 
 * Return Value: None
 *
 ******************************************************************************/
void DrawPlayerWindow(PlayerWindow *pWindow)
{
     // Display a list of songs fetched
     for (int i = 0; i < MAXMENULIST; i++)
     {
         pWindow->menu_list[i].drawButton(false, true);
     }
     
     // Display buttons
     for (int i = 0; i < PLAYERBUTTONNUM; i++)
     {
         pWindow->button_list[i].drawButton(false, false);
     }   
}

/*******************************************************************************
 * Function:  InitMenuLabel
 * 
 * Description: Init menu buttons
 * 
 * Arguments:   None
 * 
 * Return Value: None
 *
 ******************************************************************************/
void InitMenuLabels(PlayerWindow *pWindow, boolean upDownFlag)
{
     // Fetch a list of songs from SD card and initialize in the list
     for (int i = 0; i < activeMenuBtnCnt; i++)
     {
          if(upDownFlag)
          {
              pWindow->menu_list[i].relabelButton(listOfSongs[currSongFilePntr+i]);
              pWindow->menu_list[i].drawButton(false, true);
          }
          else
          {
                // Should scroll over - feed lower menu to upwards
              pWindow->menu_list[(activeMenuBtnCnt-1) - i].relabelButton(listOfSongs[currSongFilePntr-i]); 
              pWindow->menu_list[(activeMenuBtnCnt-1) - i].drawButton(false, true);
          }
     }
     
     //count is not equal to menu count, fill remaining with NULL labelling
     if(activeMenuBtnCnt != MAXMENULIST)
     {
         for(int i = activeMenuBtnCnt; i < MAXMENULIST; ++i)
         {
             pWindow->menu_list[i].relabelButton("");
             pWindow->menu_list[i].drawButton(false, true);
         }
     } 
}

/*******************************************************************************
 * Function:  getActiveButtonCount
 * 
 * Description: Determine active list of menu buttons to be created
 * 
 * Arguments:   size
 *              index pointer
 *              updown event: true - Down event, false - Up event
 * 
 * Return Value: None
 *
 ******************************************************************************/
static INT8U getActiveButtonCount (boolean upDownFlag)
{
    int count = 0;
    if(sizeOfList == 0)
        return count;
    if(upDownFlag)
         // Determine the number of labels to be displayed
         count = (((sizeOfList-1) - currSongFilePntr) >= MAXMENULIST) ? MAXMENULIST : ((sizeOfList-1) - currSongFilePntr);
    else
         // Upper will always be filled with 7 values, wont allow creation of 
         // buttons if current point is at 0
         count = (currSongFilePntr > 0) ? MAXMENULIST : 0;
    return count;
}
/*******************************************************************************
 * Function:  buttonPressResponse
 * 
 * Description: A unique display for button on a press event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void buttonPressResponse(Adafruit_GFX_Button *button)
{
    button->refillButton(ILI9341_DARKGREY);
}

/*******************************************************************************
 * Function:  buttonReleaseResponse
 * 
 * Description: A unique display for button on a release event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void buttonReleaseResponse(Adafruit_GFX_Button *button)
{
    button->refillButton(ILI9341_WHITE);
}

/*******************************************************************************
 * Function:  setMenuToSelectState
 * 
 * Description: A unique display for menu on a select event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void setMenuToSelectState(Adafruit_GFX_Button *menu)
{
    menu->refillButton(ILI9341_BLACK);
}

/*******************************************************************************
 * Function:  updateStatusBar
 * 
 * Description: A unique display for status button on a active/inactive event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void updateStatusBar(Adafruit_GFX_Button *status, BOOLEAN state = OS_FALSE)
{
    if(state)
    {
        status->refillButton(ILI9341_BLUE);
        status->reoutlineButton(ILI9341_BLUE);
    }
    else
    {
        status->refillButton(ILI9341_WHITE);
        status->reoutlineButton(ILI9341_WHITE);
    }
}

/*******************************************************************************
 * Function:  updateVolumeBar
 * 
 * Description: A unique display for volume bar on a active/inactive event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void updateVolumeBar(Adafruit_GFX_Button *vol, BOOLEAN state = OS_FALSE)
{
    if(state)
    {
        vol->refillButton(ILI9341_BLUE);
    }
    else
    {
        vol->refillButton(ILI9341_WHITE);
    }
}

/*******************************************************************************
 * Function:  setMenuToSelectActive
 * 
 * Description: A unique display for menu on a active event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void setMenuToActiveState(Adafruit_GFX_Button *menu)
{
    menu->refillButton(ILI9341_BLUE);
}

/*******************************************************************************
 * Function:  setMenuToSelectActive
 * 
 * Description: A unique display for menu on a active event
 * 
 * Arguments:   Adafruit_GFX_Button - button background color value to be updated
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void setMenuToInactiveState(Adafruit_GFX_Button *menu)
{
    menu->refillButton(ILI9341_WHITE);
}

/*******************************************************************************
 * Function:  drawPlayStatus
 * 
 * Description: Display Current Play Status
 * 
 * Arguments:   char[] - status value to be displayed
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void drawPlayStatus(char *status)
{
    char buf[BUFFERSIZE];
    
    lcdCtrl.setCursor(STATUS_XCOORD, STATUS_YCOORD);
    lcdCtrl.setTextColor(ILI9341_BLACK, ILI9341_WHITE);  
    lcdCtrl.setTextSize(1);
    PrintToLcdWithBuf(buf, BUFFERSIZE, status);
}

// Renders a character at the current cursor position on the LCD
static void PrintCharToLcd(char c)
{
    lcdCtrl.write(c);
}
 /************************************************************************************

   Print a formated string with the given buffer to LCD.
   Each task should use its own buffer to prevent data corruption.

************************************************************************************/
void PrintToLcdWithBuf(char *buf, int size, char *format, ...)
{
    va_list args;
    va_start(args, format);
    PrintToDeviceWithBuf(PrintCharToLcd, buf, size, format, args);
    va_end(args);
}

/*******************************************************************************
 * Function:  PlayerWindowStateMachine
 * 
 * Description: State Machine for MP3 Player Window UI Inerface
 * 
 * Arguments:   Event_Type
 * 
 * Return Value: None
 *
 ******************************************************************************/
void PlayerWindowStateMachine(PlayerWindow *pWindow, Event_Type winEvent)
{
    OS_CPU_SR cpu_sr;
    switch (winEvent)
    {
    case EVENT_PLAY_PRESS:
        buttonPressResponse(&pWindow->button_list[PLAY]);
        pWindow->button_list[PLAY].drawButton();
             
        break;
    case EVENT_PLAY_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[PLAY]);
        pWindow->button_list[PLAY].drawButton();
                
        drawPlayStatus(player_status[PLAYING]);
        break;
    case EVENT_PAUSE_PRESS:
        buttonPressResponse(&pWindow->button_list[PAUSE]);
        pWindow->button_list[PAUSE].drawButton();
        break; 
    case EVENT_PAUSE_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[PAUSE]);
        pWindow->button_list[PAUSE].drawButton();
        
        drawPlayStatus(player_status[PAUSED]);
        break;
    case EVENT_STOP_PRESS:
        buttonPressResponse(&pWindow->button_list[STOP]);
        pWindow->button_list[STOP].drawButton();
        break;
    case EVENT_STOP_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[STOP]);
        pWindow->button_list[STOP].drawButton();
        
        setMenuToActiveState(&pWindow->status_box);
        pWindow->status_box.drawButton(false, false);
        
        OSTimeDly(50);
        
        setMenuToInactiveState(&pWindow->status_box);
        pWindow->status_box.drawButton(false, false);
        
        statusBarBtnCnt = -1;
        
        drawPlayStatus(player_status[STOPPED]);
        break;
    case EVENT_REWIND_PRESS:
        buttonPressResponse(&pWindow->button_list[REWIND]);
        pWindow->button_list[REWIND].drawButton();
        break;
    case EVENT_REWIND_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[REWIND]);
        pWindow->button_list[REWIND].drawButton();
        
        break;
    case EVENT_FF_PRESS:
        buttonPressResponse(&pWindow->button_list[FF]);
        pWindow->button_list[FF].drawButton();
        break;
    case EVENT_FF_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[FF]);
        pWindow->button_list[FF].drawButton();
            
        break;
    case EVENT_UP_PRESS:
        buttonPressResponse(&pWindow->button_list[UP]);
        pWindow->button_list[UP].drawButton();
        
        break;
    case EVENT_UP_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[UP]);
        pWindow->button_list[UP].drawButton();
        
        if((currMenuSelectCounter-1) == -1)
        {
            if(getActiveButtonCount(false) > 0)
            {
                activeMenuBtnCnt = getActiveButtonCount(false);
                
                // Set last button to active state before resetting button values
                setMenuToActiveState(&pWindow->menu_list[currMenuSelectCounter]);
                pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
                
                currSongFilePntr--;
                
                // Reinit with new values
                InitMenuLabels(pWindow,false);
                
                currMenuSelectCounter = activeMenuBtnCnt-1;
                // Set last menu button as selected
                setMenuToSelectState(&pWindow->menu_list[currMenuSelectCounter]);
                pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
            }
            else
            {
                //need to be dealt here
                activeMenuBtnCnt = getActiveButtonCount(true);
            }
        }
        else if(activeMenuBtnCnt != 0)
        {
            setMenuToActiveState(&pWindow->menu_list[currMenuSelectCounter]);
            pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
            
            OS_ENTER_CRITICAL();
            currSongFilePntr--;
            OS_EXIT_CRITICAL();

            currMenuSelectCounter--;
            
            setMenuToSelectState(&pWindow->menu_list[currMenuSelectCounter]);
            pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
        }
        else
        {
        }
        break;
    case EVENT_DOWN_PRESS:
        buttonPressResponse(&pWindow->button_list[DOWN]);
        pWindow->button_list[DOWN].drawButton();
        break;
    case EVENT_DOWN_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[DOWN]);
        pWindow->button_list[DOWN].drawButton();
        
        if((currMenuSelectCounter+1) == activeMenuBtnCnt)
        {
            if(getActiveButtonCount(true) > 0)
            {
                activeMenuBtnCnt = getActiveButtonCount(true);
                    
                // Set last button to active state before resetting button values
                setMenuToActiveState(&pWindow->menu_list[currMenuSelectCounter]);
                pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
                
                OS_ENTER_CRITICAL(); ;
                currSongFilePntr++;
                OS_EXIT_CRITICAL();
                
                // Reinit with new values
                InitMenuLabels(pWindow,true);
                
                currMenuSelectCounter = 0;
                
                // Set first menu button as selected
                setMenuToSelectState(&pWindow->menu_list[currMenuSelectCounter]);
                pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
            }
            else
            {
                activeMenuBtnCnt = getActiveButtonCount(false);
            }
        }
        else if(activeMenuBtnCnt != 0 && currSongFilePntr != sizeOfList -1)
        {
            setMenuToActiveState(&pWindow->menu_list[currMenuSelectCounter]);
            pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
            
            OS_ENTER_CRITICAL();
            currSongFilePntr++;
            OS_EXIT_CRITICAL();

            currMenuSelectCounter++;
            
            setMenuToSelectState(&pWindow->menu_list[currMenuSelectCounter]);
            pWindow->menu_list[currMenuSelectCounter].drawButton(false, true);
        }
            
        break;
    case EVENT_VOLPLUS_PRESS:
        buttonPressResponse(&pWindow->button_list[VOLPLUS]);
        pWindow->button_list[VOLPLUS].drawButton();
        
        break;
    case EVENT_VOLPLUS_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[VOLPLUS]);
        pWindow->button_list[VOLPLUS].drawButton();
        
        if(volumeBarBtnCnt < (INT8S)(VOLUMEBARSNUM-1))
        {
            volumeBarBtnCnt += 1;
            updateVolumeBar(&pWindow->vol_bar[volumeBarBtnCnt], OS_TRUE);
            pWindow->vol_bar[volumeBarBtnCnt].drawButton();
        }
        break;
    case EVENT_VOLMINUS_PRESS:
        buttonPressResponse(&pWindow->button_list[VOLMINUS]);
        pWindow->button_list[VOLMINUS].drawButton();
        
        break;
    case EVENT_VOLMINUS_RELEASE:
        buttonReleaseResponse(&pWindow->button_list[VOLMINUS]);
        pWindow->button_list[VOLMINUS].drawButton();
        
        if(volumeBarBtnCnt >= 0)
        {
            updateVolumeBar(&pWindow->vol_bar[volumeBarBtnCnt]);
            pWindow->vol_bar[volumeBarBtnCnt].drawButton();
            volumeBarBtnCnt--;
        }
        break;
    case EVENT_STATUSBAR_INC:
        if(statusBarBtnCnt < (INT8S)(STATUSBARSNUM-1))
        {
            statusBarBtnCnt += 1;
            updateStatusBar(&pWindow->status_bar[statusBarBtnCnt], OS_TRUE);
            pWindow->status_bar[statusBarBtnCnt].drawButton();
        }
        break;
    case EVENT_STATUSBAR_DEC:
        // MP3 ensures status increment and decrement in the range of 1/10th
        
        if(statusBarBtnCnt >= 0)
        {
            updateStatusBar(&pWindow->status_bar[statusBarBtnCnt]);
            pWindow->status_bar[statusBarBtnCnt].drawButton();
            statusBarBtnCnt--;
        }
        
        break;
    case EVENT_NONE:
        break;
    default:
        break;
    }
}<file_sep>/**
    discoveryboard.h
    Board specific definitions live here
    Developed for University of Washington embedded systems programming certificate
    
    2021/1 <NAME> wrote/arranged it

*/
#ifndef __DISCOVERYBOARD_H
#define __DISCOVERYBOARD_H

/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx.h"
#include "stm32l4xx_ll_system.h"
#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx_ll_bus.h"
#include "stm32l4xx_ll_usart.h"
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_ll_i2c.h"
#include "stm32l4xx_ll_utils.h"
#include "stm32l4xx_ll_pwr.h"

/* Exported define -----------------------------------------------------------*/

// User button
//#define GPIO_PIN_USER_BUTTON    LL_GPIO_PIN_13
//#define GPIO_PORT_USER_BUTTON   GPIOC

// Green LED
#define GPIO_PIN_LD2            LL_GPIO_PIN_14
#define GPIO_PORT_LD2           GPIOB

// USART GPIO
#define GPIO_TX_AF_SOURCE       LL_GPIO_PIN_6
#define GPIO_RX_AF_SOURCE       LL_GPIO_PIN_7
#define GPIO_USART_AF           GPIO_AF_USART1
#define GPIO_PIN_TX             LL_GPIO_PIN_6
#define GPIO_PIN_RX             LL_GPIO_PIN_7
#define GPIO_PORT_USART         GPIOA

// USART 
#define BAUD_RATE               115200
#define COMM                    USART1

#endif<file_sep>/*
    bspI2c.c

    Board support for controlling I2C interfaces

    Adapted for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> adapted it
    2020/8 <NAME> adapted it for ST32L475VGT6
  
*/

#include "bsp.h"

#define MAX_TIMEOUT_ITERATIONS  10000

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
void BspI2C1_init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  LL_I2C_InitTypeDef I2C_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
  /**I2C1 GPIO Configuration
  PB8   ------> I2C1_SCL
  PB9   ------> I2C1_SDA
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_8|LL_GPIO_PIN_9;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  /** I2C Initialization
  */
  LL_I2C_EnableAutoEndMode(I2C1);
  LL_I2C_DisableOwnAddress2(I2C1);
  LL_I2C_DisableGeneralCall(I2C1);
  LL_I2C_EnableClockStretching(I2C1);
  I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  I2C_InitStruct.Timing = 0x00303D5B;
  I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
  I2C_InitStruct.DigitalFilter = 0;
  I2C_InitStruct.OwnAddress1 = 0;
  I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  LL_I2C_Init(I2C1, &I2C_InitStruct);
  LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

// Perform a wait with timeout on the given I2C1 flag. If timeout occurs, reset I2C1.
// IsActive is a pointer to a LL_I2C_IsActiveFlag_X() function.
// value is 0 or 1 and represents the desired active state to wait for. 0 means inactive, 1 means active.
void BspI2c_WaitWithTimeoutReset(uint32_t (*IsActive)(I2C_TypeDef *I2Cx), uint32_t value) {
  int32_t timeoutCount = MAX_TIMEOUT_ITERATIONS;
  
  if (value) {
    while (!(*IsActive)(I2C1) && --timeoutCount);
  } else {
    while ((*IsActive)(I2C1) && --timeoutCount);
  }

  if (timeoutCount <= 0) 
    BspI2C1_init();
}



/* This function issues a start condition and 
 * transmits the slave address + R/W bit
 * 
 * Parameters:
 * 		I2Cx --> the I2C peripheral e.g. I2C1
 * 		address --> the 7 bit slave address
 * 		direction --> the transmission direction can be:
 * 						LL_I2C_GENERATE_START_WRITE for Master transmitter mode
 * 						LL_I2C_GENERATE_START_READ for Master receiver mode
 */
void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint32_t direction, uint32_t request, uint8_t nBytes){
  BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_BUSY, 0);
  LL_I2C_HandleTransfer(I2Cx, address, LL_I2C_ADDRSLAVE_7BIT, (uint32_t)nBytes, request, direction);  //LL_I2C_MODE_AUTOEND
}

/* This function transmits one byte to the slave device
 * Parameters:
 *		I2Cx --> the I2C peripheral e.g. I2C1 
 *		data --> the data byte to be transmitted
 */
void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
  // Wait for transmit reg to be empty
  BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_TXE, 1);
  LL_I2C_TransmitData8(I2Cx, data);
  BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_TXE, 1);
}

/* This function reads one byte from the slave device.
 * I2C_start must be called first to set up the read. If the NBYTE register 
 * of the specified I2C device becomes 0, a NACK is sent to the slave after
 * the read, otherwise, ACK is sent.
 */
uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
        LL_I2C_AcknowledgeNextData(I2Cx, LL_I2C_ACK);
	BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_RXNE, 1);
	// read data from I2C data register and return data byte
	uint8_t data = LL_I2C_ReceiveData8(I2Cx);
	return data;
}

/* This function reads one byte from the slave device
 * and doesn't acknowledge the received data 
 * after that a STOP condition is transmitted
 */
uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
	// disable acknowledge of received data
	// nack also generates stop condition after last byte received
	// see reference manual for more info
        LL_I2C_AcknowledgeNextData(I2Cx, LL_I2C_NACK);
	// wait until one byte has been received
	BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_RXNE, 1);
	// read data from I2C data register and return data byte
	uint8_t data = LL_I2C_ReceiveData8(I2Cx);
	return data;
}

/* This function issues a stop condition and therefore
 * releases the bus
 */
void I2C_stop(I2C_TypeDef* I2Cx){
  LL_I2C_GenerateStopCondition(I2Cx);
  BspI2c_WaitWithTimeoutReset(LL_I2C_IsActiveFlag_STOP, 1);
}

<file_sep>/*
    mp3UserInterface.h
    MP3 Player User Interfacing Objects and functions for controlling the LCD Display related activities.

    Developed for University of Washington embedded systems programming certificate
    
    2021/2 <NAME> it
*/

#ifndef __MP3USERINTERFACE_H
#define __MP3USERINTERFACE_H

#include "bsp.h"
#include "print.h"

#include "events.h"
#include "globals.h"

#include "mp3UserInterface.h"
#include "mp3UiSettings.h"

#define BUFFERSIZE  32U

// Play Status
typedef enum
{
    SELECT = 0,
    PLAYING,
    PAUSED,
    STOPPED
} PlayStatus;

typedef struct{
    char     *label;
    uint16_t x_coordinate;
    uint16_t y_coordinate;
    uint8_t  width;
    uint8_t  height;
}ButtonParameter;
                                      

// LCD Controller Setup and Inialization
void SetUpLCDInterface ();
        
// MP3 Player Window Control Functions 
void InitPlayerWindow(PlayerWindow *pWindow);
//void DeinitPlayerWindow(PlayerWindow *pWindow);
void DrawPlayerWindow(PlayerWindow *pWindow);

void PlayerWindowStateMachine(PlayerWindow *pWindow, Event_Type winEvent);

void PrintToLcdWithBuf(char *buf, int size, char *format, ...);

#endif
<file_sep>/*
    bspLcd.c

    Board support for controlling the ILI9341 LCD on the Adafruit '2.8" TFT LCD w/Cap Touch' via NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#include "bsp.h"



// Initializes GPIO pins for the ILI9341 LCD device.
void BspLcdInitILI9341()
{
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);

    LL_GPIO_InitTypeDef GPIO_InitStruct;
    
    /*-------- Configure CS ChipSelect Pin --------*/ 
 
    GPIO_InitStruct.Pin = LCD_ILI9341_CS_GPIO_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
     
    LL_GPIO_Init(LCD_ILI9341_CS_GPIO, &GPIO_InitStruct);
    LCD_ILI9341_CS_DEASSERT();
 
    /*-------- Configure DC Data/Command Pin --------*/ 
 
    GPIO_InitStruct.Pin = LCD_ILI9341_DC_GPIO_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
     
    LL_GPIO_Init(LCD_ILI9341_DC_GPIO, &GPIO_InitStruct);
}<file_sep>/*
    bspSD.c

    Board support for controlling the SD on the Adafruit shields via NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#include "bsp.h"



// Initializes GPIO pins for the SD device.
void BspSDInitAdafruit()
{
  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    LL_GPIO_InitTypeDef GPIO_InitStruct;
    
    /*-------- Configure CS ChipSelect Pin --------*/ 
 
    GPIO_InitStruct.Pin = SD_ADAFRUIT_CS_GPIO_Pin;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
     
    LL_GPIO_Init(SD_ADAFRUIT_CS_GPIO, &GPIO_InitStruct);
    SD_ADAFRUIT_CS_DEASSERT();
}<file_sep>/*
    bspUart.c

    Board support for controlling UART interfaces

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it for NUCLEO-F401RE
    2020/8 <NAME> wrote/arranged it for STM32L475
*/

#include "bsp.h"

void UartInit(uint32_t baud)
{
  USARTx_GPIO_CLK_ENABLE();

  /* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
  LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
  USARTx_SET_TX_GPIO_AF();
  LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);

  /* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
  LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
  USARTx_SET_RX_GPIO_AF();
  LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);

  /* (2) Enable USART peripheral clock and clock source ***********************/
  USARTx_CLK_ENABLE();

  /* Set clock source */
  USARTx_CLK_SOURCE();

  /* (3) Configure USART functional parameters ********************************/
  
  /* Disable USART prior modifying configuration registers */
  /* Note: Commented as corresponding to Reset value */
  // LL_USART_Disable(USARTx_INSTANCE);

  /* TX/RX direction */
  LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);

  /* 8 data bit, 1 start bit, 1 stop bit, no parity */
  LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);

  /* No Hardware Flow control */
  /* Reset value is LL_USART_HWCONTROL_NONE */
  // LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);

  /* Oversampling by 16 */
  /* Reset value is LL_USART_OVERSAMPLING_16 */
  // LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);

  /* Set Baudrate to 115200 using APB frequency set to 80000000 Hz */
  /* Frequency available for USART peripheral can also be calculated through LL RCC macro */
  /* Ex :
      Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance
  
      In this example, Peripheral Clock is expected to be equal to 80000000 Hz => equal to SystemCoreClock
  */
  
  uint32_t Periphclk = LL_RCC_GetUSARTClockFreq(USARTx_CLKSOURCE);

  LL_USART_SetBaudRate(USARTx_INSTANCE, Periphclk, LL_USART_OVERSAMPLING_16, baud); 

  /* (4) Enable USART *********************************************************/
  LL_USART_Enable(USARTx_INSTANCE);

  /* Polling USART initialisation */
  while((!(LL_USART_IsActiveFlag_TEACK(USARTx_INSTANCE))) || (!(LL_USART_IsActiveFlag_REACK(USARTx_INSTANCE))))
  { 
  }
}

/**
  * @brief  Print a character on the HyperTerminal
  * @param  c: The character to be printed
  * @retval None
  */
void PrintByte(char c)
{
  LL_USART_TransmitData8(COMM, c);
  while (!LL_USART_IsActiveFlag_TXE(COMM));
}

/**
  * @brief  Busy wait for a character on the HyperTerminal
  * @retval: The character that was read
  */
char ReadByte()
{
    while (!LL_USART_IsEnabledIT_RXNE(COMM));
    uint16_t c =LL_USART_ReceiveData8(COMM);
    LL_USART_DisableIT_RXNE(COMM);
    return c;
}<file_sep>/*
    hw_init.h
       
    2021/1 <NAME> wrote/arranged it
*/
#ifndef __HW_INIT_H
#define __HW_INIT_H


// Application interface to hardware

void Hw_init(void);
void SetSysTick(uint32_t ticksPerSec);

#endif /* __HW_INIT_H */<file_sep>/*
    mp3TouchInterface.c
    MP3 Player Touch Interface Implementation

    Developed for University of Washington embedded systems programming certificate
    
    2021/3 <NAME> wrote/arranged it
*/

#include "mp3TouchInterface.h"

static long MapTouchToScreen(long x, long in_min, long in_max, long out_min, long out_max);
Event_Type event;

// The touch controller Instance
Adafruit_FT6206 touchCtrl = Adafruit_FT6206();

// Button Status
// It's 0th - 8th bits (mapped with PlayerButtonIndex) are used to track button status
// Set to 1 if button press activity is carried out, else default 0 (release/noactivity)
static unsigned int ButtonStatus = 0;
//static HANDLE hI2c = 0;

// Function Definitions
static boolean IsAnyButtonsPressed (void);
static void SetButtonStatus (unsigned int index);
static void ResetButtonStatus (unsigned int index);
static unsigned int WhichButtonWasPressed (void);

/*******************************************************************************
 * Function:  SetUpTouchInterface
 * 
 * Description: Set Up Touch Controller and related interface.
 * 
 * Arguments:  
 * 
 * Return Value: None
 *
 ******************************************************************************/
void SetUpTouchInterface(void)
{
    PjdfErrCode pjdfErr;
        
    // Open a HANDLE for accessing device PJDF_DEVICE_ID_I2C1
    HANDLE hI2c = Open(PJDF_DEVICE_ID_I2C1, 0);
    if (!PJDF_IS_VALID_HANDLE(hI2c)) while(1);
    
    // Call Ioctl on that handle to set the I2C device address to FT6206_ADDR
    INT8U addressFT6206 = (FT6206_ADDR << 1);
    pjdfErr = Ioctl(hI2c, PJDF_CTRL_I2C_SET_DEVICE_ADDRESS, &addressFT6206, NULL);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);
    
    // Call setPjdfHandle() on the touch contoller to pass in the I2C handle
    touchCtrl.setPjdfHandle(hI2c);

    if (! touchCtrl.begin(40)) {  // pass in 'sensitivity' coefficient
        //PrintWithBuf(buf, BUFSIZE, "Couldn't start FT6206 touchscreen controller\n");
        while (1);
    }
}

/*******************************************************************************
 * Function:  TouchEventGenerator
 * 
 * Description: It maps the touch points w.r.t screen orientation, if touch points
 *              match with any MP3 Player window buttons - updates button's press 
 *              status. And then based on press/release status, it sends events in mailbox
 * Note:        Ensures not to send multiple press/release events of a button
 *              Also handles one button press/release event at a time 
 * 
 * Arguments:  
 * 
 * Return Value: None
 *
 ******************************************************************************/
void TouchEventGenerator (PlayerWindow pWindow)
{
    boolean touched;
    
    // Get touch status
    touched = touchCtrl.touched();
    
    if(IsAnyButtonsPressed() & !touched)
    {
        unsigned int button;
        // implies button was released, so send a release event for the respective button
        button = WhichButtonWasPressed();
        
        // button events were numbered w.r.t to number of events in system - release is just a numeric up
        event  = (Event_Type) (button * NUM_TYPE_OF_EVENTS + BUTTON_RELEASED);
        OSMboxPost(touchEventsMbox, (void*)&event);
        // Rest button Status
        ResetButtonStatus(button);
    }
    else if( !(IsAnyButtonsPressed()) & touched)
    {
        TS_Point point;
        TS_Point p = TS_Point();
        
        point = touchCtrl.getPoint();
        
        if (point.x == 0 && point.y == 0)
        {
            //break; // usually spurious, so ignore
        }
        
        // transform touch orientation to screen orientation.
        p.x = MapTouchToScreen(point.x, 0, ILI9341_TFTWIDTH, ILI9341_TFTWIDTH, 0);
        p.y = MapTouchToScreen(point.y, 0, ILI9341_TFTHEIGHT, ILI9341_TFTHEIGHT, 0);
        
        // If button is pressed, set button status and send button status event
        for(unsigned int button = PLAY; button < PLAYERBUTTONNUM; ++button)
        {
            if (pWindow.button_list[button].contains(p.x, p.y)) {
                
                event  = (Event_Type) (button * NUM_TYPE_OF_EVENTS + BUTTON_PRESSED);
                OSMboxPost(touchEventsMbox, (void*)&event);
                
                SetButtonStatus(button);
            }
        }            
    }
    else
    {
        // TBD
    }
        
}

/*******************************************************************************
 * Function:  MapTouchToScreen 
 * 
 * Description: Transform touch orientation to the screen orientation 
 * 
 * Arguments:    x - x or y coordinate value
 *               in_main - starting x or y axis value
 *               in_max - end x or y axis value
 *               out_min - end x or y axis value
 *               out_max - starting x or y axis value
 * 
 * Return Value: screen equivalent touch point (x or y coordinate)
 *
 ******************************************************************************/
static long MapTouchToScreen(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

/*******************************************************************************
 * Function:  IsAnyButtonsPressed
 * 
 * Description: Sends true, if any of the button is pressed at any given time
 *
 * Note:        Buttons could be either pressed or released state
 * 
 * Arguments:  None 
 * 
 * Return Value: boolean
 *
 ******************************************************************************/
static boolean IsAnyButtonsPressed (void)
{
    return (ButtonStatus > 0);
}

/*******************************************************************************
 * Function:  SetButtonStatus
 * 
 * Description: Sets respective index (identified as button) of ButtonStatus
 * 
 * Arguments:  index 
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void SetButtonStatus (unsigned int index)
{
    ButtonStatus |= (1 << index);
}

/*******************************************************************************
 * Function:  ResetButtonStatus
 * 
 * Description: Resets respective index (identified as button) of ButtonStatus
 * 
 * Arguments:  index 
 * 
 * Return Value: None
 *
 ******************************************************************************/
static void ResetButtonStatus (unsigned int index)
{
    ButtonStatus &= ~(1 << index);
}

/*******************************************************************************
 * Function:  WhichButtonWasPressed
 * 
 * Description: Get the index of button that was pressed
 *              To be used if any button press event had occured (
 *              i.e. call IsAnyButtonsPressed() before)
 * 
 * Arguments:  None 
 * 
 * Return Value: unsigned int -> button index value
 *
 ******************************************************************************/
static unsigned int WhichButtonWasPressed (void)
{
    unsigned int index = PLAY;
    int item = ButtonStatus;
    while (item > 1)
    {
        item >>= 1;
        ++index;
    }
    return index;
}<file_sep>/*
    events.h
    Header file for for event-definition of the application and some helping functions for managing OS Events.

    Developed for University of Washington embedded systems programming certificate
    
    2021/3 <NAME> wrote/arranged it
*/

#ifndef __EVENTS_H
#define __EVENTS_H

#define EVENT_QUEUE_SIZE     20U
#define NUM_TYPE_OF_EVENTS   10U // display events group

// Specific for controlling Display events
typedef enum
{
        
    // Play Button Event
    EVENT_PLAY_PRESS = 0,
    EVENT_PLAY_RELEASE,
    
    // Pause Button Event
    EVENT_PAUSE_PRESS = 10,
    EVENT_PAUSE_RELEASE,
    
    // Stop Button Event
    EVENT_STOP_PRESS = 20,
    EVENT_STOP_RELEASE,
    
    // Rewind Button Event
    EVENT_REWIND_PRESS = 30,
    EVENT_REWIND_RELEASE,
    
    // Fast Forward Button Event
    EVENT_FF_PRESS = 40,
    EVENT_FF_RELEASE,
    
    // UP Button Event
    EVENT_UP_PRESS = 50,
    EVENT_UP_RELEASE,
    
    // Down Button Event
    EVENT_DOWN_PRESS = 60,
    EVENT_DOWN_RELEASE,
    
    // Volume Increase Button Event
    EVENT_VOLPLUS_PRESS = 70,
    EVENT_VOLPLUS_RELEASE,
    
    // Volume Decrease Button Event
    EVENT_VOLMINUS_PRESS = 80,
    EVENT_VOLMINUS_RELEASE,
    
    // Status Bar Increment and Decrement Update Event
    EVENT_STATUSBAR_INC,
    EVENT_STATUSBAR_DEC,
    
    EVENT_NONE
} Event_Type;

// MP3 Decoder Task event flags -- not using
/*
typedef enum
{
    setPlayFlag = (OS_FLAGS)0x1,
    setPauseFlag,       // pause event works if setPlayFlag is set
    setStopFlag,        // stop event works  if setStopFlag is set
    setFastForwardFlag, // fast forward event works if setPlayFlag is set
    setRewindFlag,      // Rewind flag works if setPlayFlag is set
    setVolUpFlag,
    setVolDownFlag    
} Flag_Type;
*/
// OS Events
extern OS_EVENT *touchEventsMbox;
extern OS_EVENT *mp3EventsMbox;

//extern OS_EVENT *mtxFileFetch;

// Event flags for controlling MP3 Streaming Events
/*
extern OS_FLAG_GRP *mp3Flags;
extern OS_FLAGS MP3PlayFlag;
*/

extern OS_EVENT *displayQMsg;
extern void * displayQMsgPtrs[EVENT_QUEUE_SIZE];

#endif<file_sep>/************************************************************************************

Copyright (c) 2001-2016  University of Washington Extension.

Module Name:

    tasks.c

Module Description:

    The tasks that are executed by the test application.

2016/2 <NAME> adapted it for NUCLEO-F401RE 

************************************************************************************/
#include <stdarg.h>

#include "bsp.h"
#include "print.h"
#include "mp3Util.h"

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>

Adafruit_ILI9341 lcdCtrl = Adafruit_ILI9341(); // The LCD controller

Adafruit_FT6206 touchCtrl = Adafruit_FT6206(); // The touch controller

#define PENRADIUS 3

long MapTouchToScreen(long x, long in_min, long in_max, long out_min, long out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}


#include "train_crossing.h"

#define BUFSIZE 256

/************************************************************************************

   Allocate the stacks for each task.
   The maximum number of tasks the application can have is defined by OS_MAX_TASKS in os_cfg.h

************************************************************************************/

static OS_STK   LcdTouchDemoTaskStk[APP_CFG_TASK_START_STK_SIZE];
static OS_STK   Mp3DemoTaskStk[APP_CFG_TASK_START_STK_SIZE];

     
// Task prototypes
void LcdTouchDemoTask(void* pdata);
void Mp3DemoTask(void* pdata);



// Useful functions
void PrintToLcdWithBuf(char *buf, int size, char *format, ...);

// Globals
BOOLEAN nextSong = OS_FALSE;

/************************************************************************************

   This task is the initial task running, started by main(). It starts
   the system tick timer and creates all the other tasks. Then it deletes itself.

************************************************************************************/
void StartupTask(void* pdata)
{
	char buf[BUFSIZE];

    PjdfErrCode pjdfErr;
    INT32U length;
    static HANDLE hSD = 0;
    static HANDLE hSPI = 0;

	PrintWithBuf(buf, BUFSIZE, "StartupTask: Begin\n");
	PrintWithBuf(buf, BUFSIZE, "StartupTask: Starting timer tick\n");

    // Start the system tick
    SetSysTick(OS_TICKS_PER_SEC);
    
    // Initialize SD card
    PrintWithBuf(buf, PRINTBUFMAX, "Opening handle to SD driver: %s\n", PJDF_DEVICE_ID_SD_ADAFRUIT);
    hSD = Open(PJDF_DEVICE_ID_SD_ADAFRUIT, 0);
    if (!PJDF_IS_VALID_HANDLE(hSD)) while(1);


    PrintWithBuf(buf, PRINTBUFMAX, "Opening SD SPI driver: %s\n", SD_SPI_DEVICE_ID);
    // We talk to the SD controller over a SPI interface therefore
    // open an instance of that SPI driver and pass the handle to 
    // the SD driver.
    hSPI = Open(SD_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);
    
    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hSD, PJDF_CTRL_SD_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);

    // Create the test tasks
    PrintWithBuf(buf, BUFSIZE, "StartupTask: Creating the application tasks\n");

    // The maximum number of tasks the application can have is defined by OS_MAX_TASKS in os_cfg.h
    OSTaskCreate(Mp3DemoTask, (void*)0, &Mp3DemoTaskStk[APP_CFG_TASK_START_STK_SIZE-1], APP_TASK_TEST1_PRIO);
    OSTaskCreate(LcdTouchDemoTask, (void*)0, &LcdTouchDemoTaskStk[APP_CFG_TASK_START_STK_SIZE-1], APP_TASK_TEST2_PRIO);

    // Delete ourselves, letting the work be done in the new tasks.
    PrintWithBuf(buf, BUFSIZE, "StartupTask: deleting self\n");
	OSTaskDel(OS_PRIO_SELF);
}

static void DrawLcdContents()
{
    char buf[BUFSIZE];
    OS_CPU_SR cpu_sr;
    
    // allow slow lower pri drawing operation to finish without preemption
    OS_ENTER_CRITICAL(); 
    
    lcdCtrl.fillScreen(ILI9341_BLACK);
    
    // Print a message on the LCD
    lcdCtrl.setCursor(40, 60);
    lcdCtrl.setTextColor(ILI9341_WHITE);  
    lcdCtrl.setTextSize(2);
    PrintToLcdWithBuf(buf, BUFSIZE, "Hello World!");

    OS_EXIT_CRITICAL();

}

/************************************************************************************

   Runs LCD/Touch demo code

************************************************************************************/
void LcdTouchDemoTask(void* pdata)
{
    PjdfErrCode pjdfErr;
    INT32U length;

    char buf[BUFSIZE];
    PrintWithBuf(buf, BUFSIZE, "LcdTouchDemoTask: starting\n");

    PrintWithBuf(buf, BUFSIZE, "Opening LCD driver: %s\n", PJDF_DEVICE_ID_LCD_ILI9341);
    // Open handle to the LCD driver
    HANDLE hLcd = Open(PJDF_DEVICE_ID_LCD_ILI9341, 0);
    if (!PJDF_IS_VALID_HANDLE(hLcd)) while(1);

	PrintWithBuf(buf, BUFSIZE, "Opening LCD SPI driver: %s\n", LCD_SPI_DEVICE_ID);
    // We talk to the LCD controller over a SPI interface therefore
    // open an instance of that SPI driver and pass the handle to 
    // the LCD driver.
    HANDLE hSPI = Open(LCD_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);

    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hLcd, PJDF_CTRL_LCD_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);

    PrintWithBuf(buf, BUFSIZE, "Initializing LCD controller\n");
    lcdCtrl.setPjdfHandle(hLcd);
    lcdCtrl.begin();

    DrawLcdContents();
    
    PrintWithBuf(buf, BUFSIZE, "Initializing FT6206 touchscreen controller\n");
    
    if (! touchCtrl.begin(40)) {  // pass in 'sensitivity' coefficient
        PrintWithBuf(buf, BUFSIZE, "Couldn't start FT6206 touchscreen controller\n");
        while (1);
    }
    
    int currentcolor = ILI9341_RED;

    while (1) { 
        boolean touched;
        
        touched = touchCtrl.touched();
        
        if (! touched) {
            OSTimeDly(5);
            continue;
        }
        
        TS_Point point;
        
        point = touchCtrl.getPoint();
        if (point.x == 0 && point.y == 0)
        {
            continue; // usually spurious, so ignore
        }
        
        // transform touch orientation to screen orientation.
        TS_Point p = TS_Point();
        p.x = MapTouchToScreen(point.x, 0, ILI9341_TFTWIDTH, ILI9341_TFTWIDTH, 0);
        p.y = MapTouchToScreen(point.y, 0, ILI9341_TFTHEIGHT, ILI9341_TFTHEIGHT, 0);
        
        lcdCtrl.fillCircle(p.x, p.y, PENRADIUS, currentcolor);
    }
}
/************************************************************************************

   Runs MP3 demo code

************************************************************************************/
void Mp3DemoTask(void* pdata)
{
    PjdfErrCode pjdfErr;
    INT32U length;
    
    char buf[BUFSIZE];
    PrintWithBuf(buf, BUFSIZE, "Mp3DemoTask: starting\n");

    PrintWithBuf(buf, BUFSIZE, "Opening MP3 driver: %s\n", PJDF_DEVICE_ID_MP3_VS1053);
    // Open handle to the MP3 decoder driver
    HANDLE hMp3 = Open(PJDF_DEVICE_ID_MP3_VS1053, 0);
    if (!PJDF_IS_VALID_HANDLE(hMp3)) while(1);

	PrintWithBuf(buf, BUFSIZE, "Opening MP3 SPI driver: %s\n", MP3_SPI_DEVICE_ID);
    // We talk to the MP3 decoder over a SPI interface therefore
    // open an instance of that SPI driver and pass the handle to 
    // the MP3 driver.
    HANDLE hSPI = Open(MP3_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);

    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hMp3, PJDF_CTRL_MP3_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);

    // Send initialization data to the MP3 decoder and run a test
	PrintWithBuf(buf, BUFSIZE, "Starting MP3 device test\n");
    Mp3Init(hMp3);
    int count = 0;
    
    while (1)
    {
        OSTimeDly(500);
        PrintWithBuf(buf, BUFSIZE, "Begin streaming sound file  count=%d\n", ++count);
        Mp3Stream(hMp3, (INT8U*)Train_Crossing, sizeof(Train_Crossing)); 
        PrintWithBuf(buf, BUFSIZE, "Done streaming sound file  count=%d\n", count);
    }
}


// Renders a character at the current cursor position on the LCD
static void PrintCharToLcd(char c)
{
    lcdCtrl.write(c);
}

/************************************************************************************

   Print a formated string with the given buffer to LCD.
   Each task should use its own buffer to prevent data corruption.

************************************************************************************/
void PrintToLcdWithBuf(char *buf, int size, char *format, ...)
{
    va_list args;
    va_start(args, format);
    PrintToDeviceWithBuf(PrintCharToLcd, buf, size, format, args);
    va_end(args);
}




<file_sep>/*
    mp3TouchInterface.h
    MP3 Player Touch Interfacing Objects and functions for controlling the LCD Touch related activities.

    Developed for University of Washington embedded systems programming certificate
    
    2021/3 <NAME> wrote/arranged it
*/
#ifndef __MP3TOUCHINTERFACE_H
#define __MP3TOUCHINTERFACE_H

#include "bsp.h"
#include "print.h"
#include "events.h"

#include "mp3UiSettings.h"
#include <Adafruit_FT6206.h>

// Button Status
typedef enum
{
    BUTTON_PRESSED = 0,
    BUTTON_RELEASED
} ButtonState;

// LCD Controller Setup and Inialization
void SetUpTouchInterface (void);

// Touch Event Generator
void TouchEventGenerator (PlayerWindow pWindow);

#endif<file_sep>/*
    bspSD.h

    Board support for controlling the SD on the Adafruit shields via NUCLEO-F401RE MCU

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it
*/

#include "stm32l4xx.h"

#ifndef __BSPSD_H
#define __BSPSD_H

#define SD_ADAFRUIT_CS_GPIO               GPIOA
#define SD_ADAFRUIT_CS_GPIO_Pin           LL_GPIO_PIN_3


#define SD_ADAFRUIT_CS_ASSERT()        LL_GPIO_ResetOutputPin(SD_ADAFRUIT_CS_GPIO, SD_ADAFRUIT_CS_GPIO_Pin);
#define SD_ADAFRUIT_CS_DEASSERT()      LL_GPIO_SetOutputPin(SD_ADAFRUIT_CS_GPIO, SD_ADAFRUIT_CS_GPIO_Pin);

#define SD_ADAFRUIT_DC_LOW()        LL_GPIO_ResetOutputPin(SD_ADAFRUIT_DC_GPIO, SD_ADAFRUIT_DC_GPIO_Pin);
#define SD_ADAFRUIT_DC_HIGH()       LL_GPIO_SetOutputPin(SD_ADAFRUIT_DC_GPIO, SD_ADAFRUIT_DC_GPIO_Pin);

#define SD_SPI_DEVICE_ID  PJDF_DEVICE_ID_SPI1

//#define SD_SPI_DATARATE  LL_SPI_BAUDRATEPRESCALER_DIV2  // Tune to find optimal value SD controller will work with. OK with 16mhz HCLK
#define SD_SPI_DATARATE  LL_SPI_BAUDRATEPRESCALER_DIV4  // Tune to find optimal value SD controller will work with. OK with 80MHz HCLK

void BspSDInitAdafruit();

#endif<file_sep>/************************************************************************************

Copyright (c) 2001-2016  University of Washington Extension.

Module Name:

    tasks.c

Module Description:

    The tasks that are executed by the test application.

2016/2 <NAME> adapted it for NUCLEO-F401RE 

************************************************************************************/
#include <stdarg.h>

#include "bsp.h"
#include "events.h"
#include "globals.h"
#include "print.h"

#include "mp3Util.h"
#include "mp3UserInterface.h"
#include "mp3TouchInterface.h"

#include <Adafruit_FT6206.h>

#define PENRADIUS 3

//#include "train_crossing.h"

#define BUFSIZE         256

char listOfSongs[MAXLISTOFSONGS][SUPPFILENAMESIZE];    // An array to prefetch a list of songs array
unsigned int sizeOfList = 0;
unsigned int currSongFilePntr = 0;


BOOLEAN isPlaying    = OS_FALSE;
BOOLEAN isStopSong   = OS_FALSE;
BOOLEAN isFastForward= OS_FALSE;
BOOLEAN isRewind     = OS_FALSE;
BOOLEAN isVolUp      = OS_FALSE;
BOOLEAN isVolDown    = OS_FALSE;

INT32U currPlayingSongFilePntr = INT_MAX;

/************************************************************************************

   Allocate the stacks for each task.
   The maximum number of tasks the application can have is defined by OS_MAX_TASKS in os_cfg.h

************************************************************************************/

static OS_STK   LcdDisplayTaskStk[APP_DISPLAY_TASK_EQ_STK_SIZE];
static OS_STK   Mp3StreamTaskStk[APP_MP3STREAM_TASK_EQ_STK_SIZE];
static OS_STK   CmdControllerTaskStk[APP_CMD_TASK_EQ_STK_SIZE];
static OS_STK   LcdTouchTaskStk[APP_TOUCH_TASK_EQ_STK_SIZE];

     
// Task prototypes
void LcdDisplayTask(void* pdata);
void Mp3StreamTask(void* pdata);
void CmdControllerTask(void* pdata);
void LcdTouchTask(void* pdata);

// Globals
PlayerWindow pWindow;                   // Player Window Instance

//OS Events
OS_EVENT *touchEventsMbox;
OS_EVENT *mp3EventsMbox;

OS_EVENT *displayQMsg;
void * displayQMsgPtrs[EVENT_QUEUE_SIZE];
/************************************************************************************

   This task is the initial task running, started by main(). It starts
   the system tick timer and creates all the other tasks. Then it deletes itself.

************************************************************************************/
void StartupTask(void* pdata)
{
	char buf[BUFSIZE];

    PjdfErrCode pjdfErr;
    INT32U length;
    INT8U err = 0;
    static HANDLE hSD = 0;
    static HANDLE hSPI = 0;

	//PrintWithBuf(buf, BUFSIZE, "StartupTask: Begin\n");
	//PrintWithBuf(buf, BUFSIZE, "StartupTask: Starting timer tick\n");

    // Start the system tick
    SetSysTick(OS_TICKS_PER_SEC);
    
    // Initialize SD card
    //PrintWithBuf(buf, PRINTBUFMAX, "Opening handle to SD driver: %s\n", PJDF_DEVICE_ID_SD_ADAFRUIT);
    hSD = Open(PJDF_DEVICE_ID_SD_ADAFRUIT, 0);
    if (!PJDF_IS_VALID_HANDLE(hSD)) while(1);


    //PrintWithBuf(buf, PRINTBUFMAX, "Opening SD SPI driver: %s\n", SD_SPI_DEVICE_ID);
    // We talk to the SD controller over a SPI interface therefore
    // open an instance of that SPI driver and pass the handle to 
    // the SD driver.
    hSPI = Open(SD_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);
    
    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hSD, PJDF_CTRL_SD_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);
    
    if (!SD.begin(hSD)) 
    {
        //PrintWithBuf(buf, PRINTBUFMAX, "Attempt to initialize SD card failed.\n");
    }

    // Create the test tasks
    PrintWithBuf(buf, BUFSIZE, "StartupTask: Creating the application tasks\n");
    
    // Create Mailbox
    touchEventsMbox = OSMboxCreate(NULL);
    mp3EventsMbox   = OSMboxCreate(NULL);
    
    //Create Queue
    displayQMsg = OSQCreate(displayQMsgPtrs, EVENT_QUEUE_SIZE);
    
    //Create Event Flag -- not using
    //mp3Flags = OSFlagCreate( 0x1, &err);

    // The maximum number of tasks the application can have is defined by OS_MAX_TASKS in os_cfg.h
    OSTaskCreate(Mp3StreamTask, (void*)0, &Mp3StreamTaskStk[APP_MP3STREAM_TASK_EQ_STK_SIZE-1], APP_TASK_TEST4_PRIO);
    OSTaskCreate(LcdDisplayTask, (void*)0, &LcdDisplayTaskStk[APP_DISPLAY_TASK_EQ_STK_SIZE-1], APP_TASK_TEST2_PRIO);
    OSTaskCreate(LcdTouchTask,   (void*)0, &LcdTouchTaskStk[APP_TOUCH_TASK_EQ_STK_SIZE-1],   APP_TASK_TEST1_PRIO);
    OSTaskCreate(CmdControllerTask,   (void*)0, &CmdControllerTaskStk[APP_CMD_TASK_EQ_STK_SIZE-1],   APP_TASK_TEST3_PRIO);

    // Delete ourselves, letting the work be done in the new tasks.
    PrintWithBuf(buf, BUFSIZE, "StartupTask: deleting self\n");
	OSTaskDel(OS_PRIO_SELF);
}

/************************************************************************************

   Runs LCD Display code

************************************************************************************/
void LcdDisplayTask(void* pdata)
{
    INT8U err;
    Event_Type winEvent;
    
    Mp3FetchFileNames();
        
    SetUpLCDInterface();
    
    InitPlayerWindow(&pWindow);
        
    while (1) { 
        
        winEvent = *((Event_Type*)OSQPend(displayQMsg, 0, &err));
        
        if(err != OS_ERR_NONE) while(1);
        
        PlayerWindowStateMachine(&pWindow, winEvent);
        
        OSTimeDly(10);
    }
}

/************************************************************************************

   Runs LCD Display code

************************************************************************************/
void LcdTouchTask(void* pdata)
{     
    SetUpTouchInterface();
    
    while (1) {         
              
        TouchEventGenerator(pWindow);
        
        OSTimeDly(10);
    }
}

/************************************************************************************

   Runs LCD Display code

************************************************************************************/
void CmdControllerTask(void* pdata)
{     
    Event_Type receivedEvent = EVENT_NONE;
    INT8U err = 0;
    
    while (1) {

        receivedEvent = *((Event_Type*)OSMboxPend(touchEventsMbox, 0, &err));
        if(err != OS_ERR_NONE) while(1);

     
        // Send their respective operating events to MP3 Decoder Task and Display Tasks
        switch (receivedEvent)
        {
        case EVENT_PLAY_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_PLAY_RELEASE:
            
            //if(!isPaused){
            if(currPlayingSongFilePntr != currSongFilePntr)
            {
                // exit from song loop
                isStopSong = OS_TRUE;
                OSMboxPost(mp3EventsMbox, (void*)&receivedEvent);
                
            }
            OSTimeDly(50);

            isPlaying = OS_TRUE;
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            
            break;
        case EVENT_PAUSE_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_PAUSE_RELEASE:
            isPlaying = OS_FALSE;
            
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_STOP_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_STOP_RELEASE:
            if(isPlaying || (currPlayingSongFilePntr == currSongFilePntr))
            {
                isStopSong = OS_TRUE;
            }
            else
            {
                err = OSQPost(displayQMsg, (void*)&receivedEvent);
            }
            break;
        case EVENT_REWIND_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_REWIND_RELEASE:
            isRewind = OS_TRUE;            
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_FF_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_FF_RELEASE:
            isFastForward = OS_TRUE;
            
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_UP_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_UP_RELEASE:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_DOWN_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_DOWN_RELEASE:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_VOLPLUS_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_VOLPLUS_RELEASE:
            if(isPlaying)
                isVolUp  = OS_TRUE;
            else
                OSMboxPost(mp3EventsMbox, (void*)&receivedEvent);
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_VOLMINUS_PRESS:
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_VOLMINUS_RELEASE:
            if(isPlaying)
                isVolDown  = OS_TRUE;
            else
                OSMboxPost(mp3EventsMbox, (void*)&receivedEvent);
            err = OSQPost(displayQMsg, (void*)&receivedEvent);
            break;
        case EVENT_NONE:
            break;
        default:
            break;
        }       
        
        OSTimeDly(5);
    }
}
/************************************************************************************

   Runs MP3 demo code

************************************************************************************/
void Mp3StreamTask(void* pdata)
{
    Event_Type  mp3Event;
    PjdfErrCode pjdfErr;
    INT8U err;
    INT32U length;
    
    char buf[BUFSIZE];
    PrintWithBuf(buf, BUFSIZE, "Mp3DemoTask: starting\n");

    PrintWithBuf(buf, BUFSIZE, "Opening MP3 driver: %s\n", PJDF_DEVICE_ID_MP3_VS1053);
    // Open handle to the MP3 decoder driver
    HANDLE hMp3 = Open(PJDF_DEVICE_ID_MP3_VS1053, 0);
    if (!PJDF_IS_VALID_HANDLE(hMp3)) while(1);

	PrintWithBuf(buf, BUFSIZE, "Opening MP3 SPI driver: %s\n", MP3_SPI_DEVICE_ID);
    // We talk to the MP3 decoder over a SPI interface therefore
    // open an instance of that SPI driver and pass the handle to 
    // the MP3 driver.
    HANDLE hSPI = Open(MP3_SPI_DEVICE_ID, 0);
    if (!PJDF_IS_VALID_HANDLE(hSPI)) while(1);

    length = sizeof(HANDLE);
    pjdfErr = Ioctl(hMp3, PJDF_CTRL_MP3_SET_SPI_HANDLE, &hSPI, &length);
    if(PJDF_IS_ERROR(pjdfErr)) while(1);

    // Send initialization data to the MP3 decoder and run a test
	PrintWithBuf(buf, BUFSIZE, "Starting MP3 device test\n");
    
//    OS_ENTER_CRITICAL();
//    Mp3FetchFileNames();
//    OS_EXIT_CRITICAL();
    
    while (1)
    {
        //OSTimeDly(50);
        mp3Event = *((Event_Type*)OSMboxPend(mp3EventsMbox, 0, &err));
        if(err != OS_ERR_NONE) while(1);
        
        // Process their respective operating events to MP3 Decoder Task and Display Tasks
        switch (mp3Event)
        {
        case EVENT_PLAY_RELEASE:
             Mp3StreamCycle(hMp3);
            break;
        
        case EVENT_VOLPLUS_RELEASE:
            Mp3VolumeControl(hMp3, VOLUP);
            break;
        
        case EVENT_VOLMINUS_RELEASE:
            Mp3VolumeControl(hMp3, VOLDOWN);
            break;
            
        default:
            break;
        }
         
    }
}
<file_sep>/*
    globals.h
    Global definitions for the application 

    Developed for University of Washington embedded systems programming certificate
    
    2021/2 <NAME> wrote/arranged it
*/

#ifndef __GLOBALS_H
#define __GLOBALS_H

#define MAXLISTOFSONGS   100
#define SUPPFILENAMESIZE 13
#define INT_MAX          0x7FFFFFFF

extern char listOfSongs[MAXLISTOFSONGS][SUPPFILENAMESIZE];    // An array to prefetch a list of songs array
extern unsigned int sizeOfList;                           // Max allowed songs list = 100, this variable will store the number fetched songs
extern unsigned int currSongFilePntr;                     // points to current song to be played

#endif<file_sep>/*
    bspLed.h

    Board support for controlling LEDs

    Developed for University of Washington embedded systems programming certificate
    
    2020/8 <NAME> wrote/arranged it for STM32L475
*/

#ifndef __BSPLED_H
#define __BSPLED_H

#define LED2_PIN                           LL_GPIO_PIN_5
#define LED2_GPIO_PORT                     GPIOA
#define LED2_GPIO_CLK_ENABLE()             LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA)

void LedInit();
void SetLED(BOOLEAN On);

#endif /* __BSPLED_H */<file_sep>/*
    mp3Util.c
    Some utility functions for controlling the MP3 decoder.

    Developed for University of Washington embedded systems programming certificate
    
    2016/2 <NAME> wrote/arranged it

    2021/2 <NAME> updated to support MP3 Streaming task for the project
*/

#include "mp3Util.h"

#define DEFAULT_VOLUME_INDEX 8

void delay(uint32_t time);


static File dataFile;
static INT8U  mp3Buf[MP3_DECODER_BUF_SIZE];
static INT32U iBufPos = 0;
static INT32U iDataFileMovPos = 0;
static INT32U iDataFileCurPos = 0;
static INT32U iDataFileBegPos = 0;
static INT32U progressCounter = 0;
static INT8U  volProgressCounter = DEFAULT_VOLUME_INDEX;
static Event_Type mp3Event;


extern BOOLEAN isFileStart;
extern BOOLEAN isPlaying;
extern BOOLEAN isStopSong;
extern BOOLEAN isFastForward;
extern BOOLEAN isRewind;
extern BOOLEAN isVolUp;
extern BOOLEAN isVolDown;

extern INT32U currPlayingSongFilePntr;

void Mp3StreamInit(HANDLE hMp3)
{
    INT32U length;
    
    // Place MP3 driver in command mode (subsequent writes will be sent to the decoder's command interface)
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_COMMAND, 0, 0);
    
    // Reset the device
    length = BspMp3SoftResetLen;
    Write(hMp3, (void*)BspMp3SoftReset, &length);
    
    length = BspMp3SetClockFLen;
    Write(hMp3, (void*)BspMp3SetClockF, &length);
 
    // Set volume
    length = BspMp3SetVolLen;
    Write(hMp3, (void*)BspMp3SetVolRange[volProgressCounter], &length);

    // To allow streaming data, set the decoder mode to Play Mode
    length = BspMp3PlayModeLen;
    Write(hMp3, (void*)BspMp3PlayMode, &length);
   
    // Set MP3 driver to data mode (subsequent writes will be sent to decoder's data interface)
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_DATA, 0, 0);
}

// Volume Controller for the application
void Mp3VolumeControl(HANDLE hMp3, VolumeCounter state)
{
    INT32U length;
    
    if(state == VOLUP)
    {
        // Volume Range Index Sanity Check
        if(volProgressCounter == MP3_VOLUME_RANGE-1) return;
        
        ++volProgressCounter;
    }
    else
    {
        // Volume Range Index Sanity Check
        if(volProgressCounter == 0) return;
        
        --volProgressCounter;
    }
    
    // Place MP3 driver in command mode
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_COMMAND, 0, 0);
    
    // Set volume
    length = BspMp3SetVolLen;
    Write(hMp3, (void*)BspMp3SetVolRange[volProgressCounter], &length);

    // To allow streaming data, set the decoder mode to Play Mode
    length = BspMp3PlayModeLen;
    Write(hMp3, (void*)BspMp3PlayMode, &length);
   
    // Set MP3 driver to data mode
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_DATA, 0, 0);
        
}

// Mp3GetRegister
// Gets a register value from the MP3 decoder.
// hMp3: an open handle to the MP3 decoder
// cmdInDataOut: on entry this buffer must contain the 
//     command to get the desired register from the decoder. On exit
//     this buffer will be OVERWRITTEN by the data output by the decoder in 
//     response to the command. Note: the buffer must not reside in readonly memory.
// bufLen: the number of bytes in cmdInDataOut.
// Returns: PJDF_ERR_NONE if no error otherwise an error code.
PjdfErrCode Mp3GetRegister(HANDLE hMp3, INT8U *cmdInDataOut, INT32U bufLen)
{
    PjdfErrCode retval;
    if (!PJDF_IS_VALID_HANDLE(hMp3)) while (1);
    
    // Place MP3 driver in command mode (subsequent writes will be sent to the decoder's command interface)
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_COMMAND, 0, 0);
    
    retval = Read(hMp3, cmdInDataOut, &bufLen);
    return retval;
}

// Mp3FetchFileNames
// Fetches a list of file names from the SD card 
// Note:  This function needs to be called at the beginning of the MP3Task()
// list - A string array where a list of songs will be stored
// size - tracks size of array as it grows
// maxSize - maximum allowable list
//void Mp3FetchFileNames(char **list, int maxRow, int col, int *sizeOfList)
void Mp3FetchFileNames()
{
    
    // Endless loop to read files from the root directory and stream them
    File dir = SD.open("/");
    char buf[13];
    while (sizeOfList <= MAXLISTOFSONGS)
    {
        File entry = dir.openNextFile();
        if (!entry)
        {
            break;
        }
        if (entry.isDirectory()) // skip directories
        {
            entry.close();
            continue;
        }
        
        char *filename = entry.name();
        for(int i = 0; i < SUPPFILENAMESIZE; ++i)
            listOfSongs[sizeOfList][i] = filename[i];
        PrintWithBuf(buf, 13, listOfSongs[sizeOfList]);
        
        sizeOfList++;
        entry.close();
    }
    dir.seek(0); // reset directory file to read again;
}

// Mp3StreamSDFile
// Streams the given file from the SD card to the given MP3 decoder.
// hMP3: an open handle to the MP3 decoder
// pFilename: The file on the SD card to stream. 

void Mp3StreamCycle(HANDLE hMp3)
{
    
    INT32U length;
    INT8U err = 0;
    
    Mp3StreamInit(hMp3);
    
	//char printBuf[PRINTBUFMAX];
    
    dataFile = SD.open(listOfSongs[currSongFilePntr], O_READ);
    if (!dataFile) 
    {
        //PrintWithBuf(printBuf, PRINTBUFMAX, "Error: could not open SD card file '%s'\n", listOfSongs[currSongFilePntr]);
        return;
    }

    // Initialize flags
    
    isStopSong = OS_FALSE;
    isFastForward = OS_FALSE;
    isRewind = OS_FALSE;
    isVolUp  = OS_FALSE; 
    isVolDown  = OS_FALSE; 
    
    progressCounter = 1;
    currPlayingSongFilePntr = currSongFilePntr;
    
    
    // this value will be used for increment/decrement song position .
    // A song data will be seen as ten parts and it will move accordingly
    iDataFileMovPos = dataFile.size()/10;
    iDataFileBegPos = dataFile.position();
    iDataFileCurPos = iDataFileBegPos;
        
    while (dataFile.available())
    {
        
        iBufPos = 0;
        //iDataFileCurPos =  dataFile.position();
        
        // if Paused stays in the loop and then picks when played again
        if(isPlaying)
        {
            while (dataFile.available() && iBufPos < MP3_DECODER_BUF_SIZE)
            {
                mp3Buf[iBufPos] = dataFile.read();
                
                if((dataFile.position() - iDataFileBegPos) == (progressCounter * iDataFileMovPos))
                {
                    mp3Event = EVENT_STATUSBAR_INC;
                    err = OSQPost(displayQMsg, (void*)&mp3Event);
                    
                    progressCounter++;
                }
                
                //delay(30);
                iBufPos++;
            }
           
            Write(hMp3, mp3Buf, &iBufPos);
            //OSTimeDly(5);
            
        }

        // new data file position
        iDataFileCurPos =  dataFile.position();
        
        // Fast Forward, Rewind, Vol+, Vol- and Stop functions should work if it
        // is Playing or Paused
        
        if(isFastForward)
        {
            //set new position
            iDataFileCurPos += iDataFileMovPos;
            //set the value in file datastructure
            dataFile.seek(iDataFileCurPos);
            //Send a Status Bar Update Event to display task
            mp3Event = EVENT_STATUSBAR_INC;
            err = OSQPost(displayQMsg, (void*)&mp3Event);
            
             progressCounter++;
                
            isFastForward = OS_FALSE;
            //OSFlagPost(mp3Flags, setFastForwardFlag, OS_FLAG_SET, &err);
        }
            
        if(isRewind)
        {
            iDataFileCurPos = (iDataFileMovPos >= iDataFileCurPos) ? iDataFileBegPos : 
                                            (iDataFileCurPos - iDataFileMovPos);
            //set the value in file datastructure
            dataFile.seek(iDataFileCurPos);
            
            //Send a Status Bar Update Event to display task
            mp3Event = EVENT_STATUSBAR_DEC;
            err = OSQPost(displayQMsg, (void*)&mp3Event);
            
             progressCounter--;
                
            
            isRewind = OS_FALSE;
        }
        
        if(isVolUp)
        {
            Mp3VolumeControl(hMp3, VOLUP);
            isVolUp = OS_FALSE;
        }
        
        if(isVolDown)
        {
            Mp3VolumeControl(hMp3, VOLDOWN);
            isVolDown = OS_FALSE;
        }
        
        if(isStopSong)
        {
            isStopSong = OS_FALSE;
            break;
        }
        
    }
    currPlayingSongFilePntr = INT_MAX;
    isPlaying = OS_FALSE;
    
    dataFile.close();
    
    mp3Event = EVENT_STOP_RELEASE;
    err = OSQPost(displayQMsg, (void*)&mp3Event);
    
    //OSFlagPost(mp3Flags, setPlayFlag | setPauseFlag, OS_FLAG_WAIT_SET_ALL, &err);
    
    Ioctl(hMp3, PJDF_CTRL_MP3_SELECT_COMMAND, 0, 0);
    length = BspMp3SoftResetLen;
    Write(hMp3, (void*)BspMp3SoftReset, &length);
}
