Page 1
LCD.H
/*
* 16x2 LCD Driver Header File for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#ifndef __LCD_H
#define __LCD_H
#include "stm32f4xx.h"
#define LCD_CTRL_EN_PIN GPIO_Pin_4
#define LCD_CTRL_EN_GPIO_PORT GPIOE
#define LCD_CTRL_EN_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_EN_SOURCE GPIO_PinSource15
#define LCD_CTRL_RS_PIN GPIO_Pin_2
#define LCD_CTRL_RS_GPIO_PORT GPIOE
#define LCD_CTRL_RS_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_RS_SOURCE GPIO_PinSource14
#define LCD_CTRL_D4_PIN GPIO_Pin_10
#define LCD_CTRL_D4_GPIO_PORT GPIOE
#define LCD_CTRL_D4_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_D4_SOURCE GPIO_PinSource8
#define LCD_CTRL_D5_PIN GPIO_Pin_11
#define LCD_CTRL_D5_GPIO_PORT GPIOE
#define LCD_CTRL_D5_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_D5_SOURCE GPIO_PinSource9
#define LCD_CTRL_D6_PIN GPIO_Pin_12
#define LCD_CTRL_D6_GPIO_PORT GPIOE
#define LCD_CTRL_D6_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_D6_SOURCE GPIO_PinSource12
#define LCD_CTRL_D7_PIN GPIO_Pin_13
#define LCD_CTRL_D7_GPIO_PORT GPIOE
#define LCD_CTRL_D7_GPIO_CLK RCC_AHB1Periph_GPIOE
#define LCD_CTRL_D7_SOURCE GPIO_PinSource15
#define LCD_LINE_LENGTH (uint8_t)16
#define LCD_LINE_TWO_ADDR (uint8_t)64
#define LCD_EN_LOW() GPIO_ResetBits(LCD_CTRL_EN_GPIO_PORT, LCD_CTRL_EN_PIN)
#define LCD_EN_HIGH() GPIO_SetBits(LCD_CTRL_EN_GPIO_PORT, LCD_CTRL_EN_PIN)
#define LCD_RS_LOW() GPIO_ResetBits(LCD_CTRL_RS_GPIO_PORT, LCD_CTRL_RS_PIN)
#define LCD_RS_HIGH() GPIO_SetBits(LCD_CTRL_RS_GPIO_PORT, LCD_CTRL_RS_PIN)
#define LCD_RS(x) GPIO_WriteBit(LCD_CTRL_RS_GPIO_PORT, LCD_CTRL_RS_PIN, x)
#define LCD_EN(x) GPIO_WriteBit(LCD_CTRL_EN_GPIO_PORT, LCD_CTRL_EN_PIN, x)
#define GET_BIT(a, b) ((a >> b) & 1)
/* Delay required by HD44780 */
#ifdef LCD_DELAY_EXTERN
extern void DelayMs(uint32_t);
#else
extern void DelayMs2(uint32_t);
#endif
/* LCD_Constants */
#define LCD_DataLength_4Bit (uint8_t)0
#define LCD_LineNumber_1Line (uint8_t)0
#define LCD_LineNumber_2Lines (uint8_t)8
#define LCD_CharacterFont_5x8Dots (uint8_t)0
#define LCD_CharacterFont_5x10Dots (uint8_t)4
#define LCD_CursorDirection_Decrement (uint8_t)0
#define LCD_CursorDirection_Increment (uint8_t)2
#define LCD_DisplayShift_NoShift (uint8_t)0
#define LCD_DisplayShift_Shift (uint8_t)1
#define LCD_DisplayState_On (uint8_t)4
#define LCD_DisplayState_Off (uint8_t)0
#define LCD_CursorState_On (uint8_t)2
#define LCD_CursorState_Off (uint8_t)0
#define LCD_BlinkCursor_On (uint8_t)1
#define LCD_BlinkCursor_Off (uint8_t)0
#define LCD_MoveOrShift_MoveCursor (uint8_t)0
#define LCD_MoveOrShift_ShiftDisplay (uint8_t)8
#define LCD_ShiftDirection_Left (uint8_t)0
#define LCD_ShiftDirection_Right (uint8_t)4
/* LCD Commands */
Page 2
#define LCD_CMD_ClearDisplay (uint8_t)1
#define LCD_CMD_CursorHome (uint8_t)2
/* LCD Command prefixes */
#define LCD_PFX_EntryModeSet (uint8_t)4
#define LCD_PFX_DisplayOnOff (uint8_t)8
#define LCD_PFX_CursorDisplayShift (uint8_t)16
#define LCD_PFX_FunctionSet (uint8_t)32
#define LCD_PFX_CgramSetAddress (uint8_t)64
#define LCD_PFX_DdramSetAddress (uint8_t)128
/* Structures */
typedef struct
{
uint8_t DataLength; /* LCD communication bus length. Only 4-bit supported yet */
uint8_t LineNumber; /* Number of lines displayed on LCD. */
uint8_t CharacterFont; /* Displayed character font */
} LCD_InitTypeDef;
typedef struct
{
uint8_t CursorDirection; /* Cursor move direction during data read/write */
uint8_t DisplayShift; /* Specifies display shift during data read/write */
} LCD_EntryModeCmdTypeDef;
typedef struct
{
uint8_t DisplayState; /* Display on/off control */
uint8_t CursorState; /* Cursor on/off control */
uint8_t BlinkCursor; /* Blinking at cursor position character on/off control */
} LCD_DisplayOnOffCmdTypedef;
typedef struct
{
uint8_t MoveOrShift; /* Specifies preferred printing mode */
uint8_t ShiftDirection; /* Specifies display shifting direcion if shifting on */
} LCD_CursorDisplayShiftCmdTypeDef;
void lcd_init();
void lcd_init_struct(LCD_InitTypeDef *LCD_InitStruct);
void lcd_clear(void);
void lcd_send_nibble(uint8_t n);
void lcd_send_byte(uint8_t address, uint8_t n);
void lcd_set_ddram_address(uint8_t address);
void lcd_set_cgram_address(uint8_t address);
void lcd_goto(uint8_t cx, uint8_t cy);
void lcd_write_custom_char(uint8_t num, const uint8_t *c);
void lcd_put_char(char c);
void lcd_put_string(const char *s);
void lcd_put_signed_int(int32_t value);
void lcd_put_unsigned_int(uint32_t value);
void lcd_entry_mode_cmd(LCD_EntryModeCmdTypeDef *LCD_EntryModeCmdStruct);
void lcd_display_onoff_cmd(LCD_DisplayOnOffCmdTypedef *LCD_DisplayOnOffStruct);
void lcd_cursor_display_shift_cmd(LCD_CursorDisplayShiftCmdTypeDef *LCD_CursorDisplayShiftStruct);
#endif
Page 3
LCD.C
/*
* 16x2 LCD Driver Implementation File for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#include "lcd.h"
void lcd_lowlevel_init();
void lcd_write_cmd(uint8_t n);
void lcd_write_data(uint8_t n);
void delay_ms(uint32_t nTime)
{
volatile uint32_t n = nTime * SystemCoreClock / 10000;
while(n > 0) n--;
}
void lcd_init() {
LCD_InitTypeDef LCD_TypeDef;
LCD_TypeDef.CharacterFont = LCD_CharacterFont_5x8Dots;
LCD_TypeDef.DataLength = LCD_DataLength_4Bit;
LCD_TypeDef.LineNumber = LCD_LineNumber_2Lines;
lcd_init_struct(&LCD_TypeDef);
}
void lcd_init_struct(LCD_InitTypeDef *LCD_InitStruct)
{
uint8_t ctrl, i;
ctrl = LCD_PFX_FunctionSet |
LCD_InitStruct->DataLength |
LCD_InitStruct->LineNumber |
LCD_InitStruct->CharacterFont;
lcd_lowlevel_init();
LCD_EN(0);
LCD_RS(0);
delay_ms(15);
for (i = 0; i < 3; i++)
{
lcd_send_nibble(3);
delay_ms(5);
}
lcd_send_nibble(2);
delay_ms(5);
lcd_send_byte(0, ctrl);
ctrl = LCD_PFX_DisplayOnOff |
LCD_DisplayState_On |
LCD_CursorState_Off |
LCD_BlinkCursor_Off;
lcd_send_byte(0, ctrl);
ctrl = LCD_CMD_ClearDisplay;
lcd_send_byte(0, ctrl);
ctrl = LCD_PFX_EntryModeSet |
LCD_CursorDirection_Increment |
LCD_DisplayShift_NoShift;
lcd_send_byte(0, ctrl);
}
void lcd_clear(void)
{
lcd_send_byte(0, LCD_CMD_ClearDisplay);
lcd_send_byte(0, LCD_CMD_CursorHome);
}
void lcd_send_nibble(uint8_t n)
{
GPIO_WriteBit(LCD_CTRL_D4_GPIO_PORT, LCD_CTRL_D4_PIN, GET_BIT(n, 0));
GPIO_WriteBit(LCD_CTRL_D5_GPIO_PORT, LCD_CTRL_D5_PIN, GET_BIT(n, 1));
GPIO_WriteBit(LCD_CTRL_D6_GPIO_PORT, LCD_CTRL_D6_PIN, GET_BIT(n, 2));
GPIO_WriteBit(LCD_CTRL_D7_GPIO_PORT, LCD_CTRL_D7_PIN, GET_BIT(n, 3));
delay_ms(1);
LCD_EN_HIGH();
delay_ms(1);
Page 4
LCD_EN_LOW();
}
void lcd_send_byte(uint8_t address, uint8_t n)
{
LCD_RS(address);
delay_ms(1);
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
void lcd_set_ddram_address(uint8_t address)
{
lcd_send_byte(0, address | LCD_PFX_DdramSetAddress);
}
void lcd_set_cgram_address(uint8_t address)
{
lcd_send_byte(0, address | LCD_PFX_CgramSetAddress);
}
void lcd_goto(uint8_t cx, uint8_t cy)
{
uint8_t address;
if (cy != 1)
address = LCD_LINE_TWO_ADDR;
else
address = 0;
address += cx - 1;
lcd_set_ddram_address(address);
}
void lcd_write_custom_char(uint8_t num, const uint8_t c[])
{
uint8_t i;
if(num < 7)
{
lcd_send_byte(0, 0x40 | (num << 3));
for(i = 0; i < 8; i++)
lcd_send_byte(1, c[i]);
lcd_set_ddram_address(0);
}
}
void lcd_put_char(char c)
{
switch (c) {
case '\a':
lcd_goto(1, 1);
break;
case '\f':
lcd_send_byte(0, LCD_CMD_ClearDisplay);
delay_ms(2);
break;
case '\n':
lcd_goto(1, 2);
break;
case '\b':
lcd_send_byte(0, 0x10);
break;
default:
lcd_send_byte(1, c);
break;
}
}
void lcd_put_string(const char *s)
{
const char *p = s;
while(*p != 0)
{
lcd_put_char(*p);
p++;
}
Page 5
}
void lcd_put_signed_int(int32_t n)
{
char c1[32];
if(n == 0)
{
lcd_put_char('0');
return;
}
signed int value = n;
unsigned int absolute;
int i = 0;
if(value < 0) {
absolute = -value;
} else {
absolute = value;
}
while (absolute > 0) {
c1[i] = '0' + absolute % 10;
absolute /= 10;
i++;
}
lcd_put_char((value < 0) ? '-' : '+');
i--;
while(i >= 0){
lcd_put_char(c1[i--]);
}
}
void lcd_put_unsigned_int(uint32_t n)
{
char c1[32];
uint32_t value = n;
uint32_t i = 0;
if(n == 0)
{
lcd_put_char('0');
return;
}
while (value > 0) {
c1[i] = '0' + value % 10;
value /= 10;
i++;
}
while(i-- > 0){
lcd_put_char(c1[i]);
}
}
void lcd_lowlevel_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(LCD_CTRL_EN_GPIO_CLK | LCD_CTRL_RS_GPIO_CLK |
LCD_CTRL_D4_GPIO_CLK | LCD_CTRL_D5_GPIO_CLK |
LCD_CTRL_D6_GPIO_CLK | LCD_CTRL_D7_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_EN_PIN;
GPIO_Init(LCD_CTRL_EN_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_RS_PIN;
GPIO_Init(LCD_CTRL_RS_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_D4_PIN;
GPIO_Init(LCD_CTRL_D4_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_D5_PIN;
GPIO_Init(LCD_CTRL_D5_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_D6_PIN;
GPIO_Init(LCD_CTRL_D6_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LCD_CTRL_D7_PIN;
GPIO_Init(LCD_CTRL_D7_GPIO_PORT, &GPIO_InitStructure);
GPIO_SetBits(LCD_CTRL_EN_GPIO_PORT, LCD_CTRL_EN_PIN);
}
Page 6
void lcd_entry_mode_cmd(LCD_EntryModeCmdTypeDef *LCD_EntryModeCmdStruct)
{
uint8_t ctrl;
ctrl = LCD_PFX_EntryModeSet |
LCD_EntryModeCmdStruct->CursorDirection |
LCD_EntryModeCmdStruct->DisplayShift;
lcd_send_byte(0, ctrl);
}
void lcd_display_onoff_cmd(LCD_DisplayOnOffCmdTypedef *LCD_DisplayOnOffStruct)
{
uint8_t ctrl;
ctrl = LCD_PFX_DisplayOnOff |
LCD_DisplayOnOffStruct->BlinkCursor |
LCD_DisplayOnOffStruct->CursorState |
LCD_DisplayOnOffStruct->DisplayState;
lcd_send_byte(0, ctrl);
}
void lcd_cursor_display_shift_cmd(
LCD_CursorDisplayShiftCmdTypeDef *LCD_CursorDisplayShiftStruct)
{
uint8_t ctrl;
ctrl = LCD_PFX_CursorDisplayShift |
LCD_CursorDisplayShiftStruct->MoveOrShift |
LCD_CursorDisplayShiftStruct->ShiftDirection;
lcd_send_byte(0, ctrl);
}
Page 7
GUITAR_GEN.H
/*
* Sound Generator Header File for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#ifndef GUITAR_GEN_H_
#define GUITAR_GEN_H_
#define C_BUFFER_SIZE 1468
#define Cs_BUFFER_SIZE 1385
#define D_BUFFER_SIZE 1308
#define Ds_BUFFER_SIZE 1234
#define E_BUFFER_SIZE 1165
#define F_BUFFER_SIZE 1100
#define Fs_BUFFER_SIZE 1038
#define G_BUFFER_SIZE 980
#define Gs_BUFFER_SIZE 925
#define A_BUFFER_SIZE 873
#define As_BUFFER_SIZE 824
#define B_BUFFER_SIZE 778
#include "stm32f4xx.h"
#include "math.h"
#include "audio.h"
typedef enum {C, Cs, D, Ds, E, F, Fs, G, Gs, A, As, B} TONE;
typedef enum {BUFFER_1, BUFFER_2, BUFFER_3} LAST_BUFFER;
void guitar_gen_init(); // Initializes the sound generation module.
void guitar_gen_update_buffers(int16_t* target_buffer);
void guitar_gen_strum(TONE x, int octave); // Pluck the string.
int guitar_tone_to_buffer_size(TONE x); // Converts the tone enum to the buffer size that results in the respective pitch.
int16_t* guitar_update_next_buffer(int curr_buffer_size); // Switches to the next tone generation buffer and returns the next buffer.
#endif
Page 8
GUITAR_GEN.C
/*
* Sound Generator Implementation for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#include "guitar_gen.h"
int16_t buffer_1[2000]; // Permanent "tone" buffers used for Karplus-Strong generation.
int16_t buffer_2[2000]; // Size is arbitrary, but must be bigger than the lowest tone's
int16_t buffer_3[2000]; // buffer size.
int buffer_1_size = 1; // Sets the size of the permanent tone buffers, these values
int buffer_2_size = 1; // are altered to change the pitch that the relevant buffer
int buffer_3_size = 1; // produces.
LAST_BUFFER last_buffer = BUFFER_3; // Tells us which buffer was last used for tone synthesis.
int buff_1_count = 0; // Holds the position we are at when iterating through the
int buff_2_count = 0; // permanent buffers.
int buff_3_count = 0;
void guitar_gen_init() {
int i;
for (i=0; i < buffer_1_size; i++) { // Clear the tone buffers.
buffer_1[i] = 0;
}
for (i=0; i < buffer_2_size; i++) {
buffer_2[i] = 0;
}
for (i=0; i < buffer_3_size; i++) {
buffer_3[i] = 0;
}
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE); // Enable the random number generator. (for Karplus-Strong synthesis)
RNG_Cmd(ENABLE);
}
void guitar_gen_update_buffers(int16_t* target_buffer) {
int i;
for (i = 0; i < BUFFER_SIZE; i+=2) { // Copy tone buffers to audio buffer and update tone buffer.
int16_t current = (buffer_1[buff_1_count % buffer_1_size] // Sum and copy the tone buffers to the audio buffer.
+ buffer_2[buff_2_count % buffer_2_size]
+ buffer_3[buff_3_count % buffer_3_size])/2;
target_buffer[i] = current/2;
target_buffer[i+1] = current/2;
buffer_1[buff_1_count % buffer_1_size] = // Update the tone buffers. (Karplus-Strong)
0.99*(buffer_1[buff_1_count % buffer_1_size]
+ buffer_1[(buff_1_count + 1) % buffer_1_size]) / 2;
buffer_2[buff_2_count % buffer_2_size] =
0.99*(buffer_2[buff_2_count % buffer_2_size]
+ buffer_2[(buff_2_count + 1) % buffer_2_size]) / 2;
buffer_3[buff_3_count % buffer_3_size] =
0.99*(buffer_3[buff_3_count % buffer_3_size]
+ buffer_3[(buff_3_count + 1) % buffer_3_size]) / 2;
buff_1_count++; // Increment our position in the tone buffers.
buff_2_count++;
buff_3_count++;
}
}
void guitar_gen_strum(TONE x, int octave) {
int curr_buff_size = guitar_tone_to_buffer_size(x)/pow(2, octave+2);
int16_t * current_buffer = guitar_update_next_buffer(curr_buff_size);
int i;
for (i = 0; i < curr_buff_size; i++) { // To simulate a strum, tone buffer is filled with random noise.
while (!RNG_GetFlagStatus(RNG_FLAG_DRDY)) {}
current_buffer[i] = RNG_GetRandomNumber();
}
}
int guitar_tone_to_buffer_size(TONE x) {
switch (x) {
case C: return C_BUFFER_SIZE;
case Cs: return Cs_BUFFER_SIZE;
case D: return D_BUFFER_SIZE;
case Ds: return Ds_BUFFER_SIZE;
Page 9
case E: return E_BUFFER_SIZE;
case F: return F_BUFFER_SIZE;
case Fs: return Fs_BUFFER_SIZE;
case G: return G_BUFFER_SIZE;
case Gs: return Gs_BUFFER_SIZE;
case A: return A_BUFFER_SIZE;
case As: return As_BUFFER_SIZE;
case B: return B_BUFFER_SIZE;
}
return -1;
}
int16_t* guitar_update_next_buffer(int curr_buff_size) {
switch (last_buffer) {
case BUFFER_1:
buffer_2_size = curr_buff_size;
last_buffer = BUFFER_2;
buff_2_count = 0;
return buffer_2;
case BUFFER_2:
buffer_3_size = curr_buff_size;
last_buffer = BUFFER_3;
buff_3_count = 0;
return buffer_3;
case BUFFER_3:
buffer_1_size = curr_buff_size;
last_buffer = BUFFER_1;
buff_1_count = 0;
return buffer_1;
}
return ((void *)0);
}
Page 10
CODEC.H
/*
* CS43L22 Codec Driver Header File for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#ifndef CODEC_H_
#define CODEC_H_
#include "stm32f4xx.h"
#define I2C_SCL_PIN GPIO_Pin_6 // Define uC pins used for I2C communication with codec.
#define I2C_SDA_PIN GPIO_Pin_9
#define I2C_SCL_PINSOURCE GPIO_PinSource6
#define I2C_SDA_PINSOURCE GPIO_PinSource9
#define I2C_SCL_GPIO_PORT GPIOB
#define I2C_SDA_GPIO_PORT GPIOB
#define I2C_SCL_CLK RCC_AHB1ENR_GPIOBEN
#define I2C_SDA_CLK RCC_AHB1ENR_GPIOBEN
#define I2S_WS_PIN GPIO_Pin_4 // Define uC pins used for I2S sound transfer with codec.
#define I2S_SCLK_PIN GPIO_Pin_10
#define I2S_SD_PIN GPIO_Pin_12
#define I2S_MCLK_PIN GPIO_Pin_7
#define I2S_WS_PINSOURCE GPIO_PinSource4
#define I2S_SCLK_PINSOURCE GPIO_PinSource10
#define I2S_SD_PINSOURCE GPIO_PinSource12
#define I2S_MCLK_PINSOURCE GPIO_PinSource7
#define I2S_WS_GPIO_PORT GPIOA
#define I2S_SCLK_GPIO_PORT GPIOC
#define I2S_SD_GPIO_PORT GPIOC
#define I2S_MCLK_GPIO_PORT GPIOC
#define I2S_WS_CLK RCC_AHB1ENR_GPIOAEN
#define I2S_SCLK_CLK RCC_AHB1ENR_GPIOCEN
#define I2S_SD_CLK RCC_AHB1ENR_GPIOCEN
#define I2S_MCLK_CLK RCC_AHB1ENR_GPIOCEN
#define CODEC_RESET_PIN GPIO_Pin_4 // Define uC GPIO pin to reset audio codec.
#define CODEC_RESET_GPIO_PORT GPIOD
#define CODEC_RESET_CLK RCC_AHB1ENR_GPIODEN
#define CODEC_SPI SPI3
#define CODEC_I2C I2C1
#define CODEC_SPI_AF GPIO_AF_SPI3
#define CODEC_I2C_AF GPIO_AF_I2C1
#define CODEC_SPI_CLK RCC_APB1ENR_SPI3EN
#define CODEC_I2C_CLK RCC_APB1ENR_I2C1EN
#define CODEC_MAP_CHIP_ID 0x01 // Define audio codec map registers.
#define CODEC_MAP_PWR_CTRL1 0x02
#define CODEC_MAP_PWR_CTRL2 0x04
#define CODEC_MAP_CLK_CTRL 0x05
#define CODEC_MAP_IF_CTRL1 0x06
#define CODEC_MAP_IF_CTRL2 0x07
#define CODEC_MAP_PASSTHROUGH_A_SELECT 0x08
#define CODEC_MAP_PASSTHROUGH_B_SELECT 0x09
#define CODEC_MAP_ANALOG_SET 0x0A
#define CODEC_MAP_PASSTHROUGH_GANG_CTRL 0x0C
#define CODEC_MAP_PLAYBACK_CTRL1 0x0D
#define CODEC_MAP_MISC_CTRL 0x0E
#define CODEC_MAP_PLAYBACK_CTRL2 0x0F
#define CODEC_MAP_PASSTHROUGH_A_VOL 0x14
#define CODEC_MAP_PASSTHROUGH_B_VOL 0x15
#define CODEC_MAP_PCMA_VOL 0x1A
#define CODEC_MAP_PCMB_VOL 0x1B
#define CODEC_MAP_BEEP_FREQ_ONTIME 0x1C
#define CODEC_MAP_BEEP_VOL_OFFTIME 0x1D
#define CODEC_MAP_BEEP_TONE_CFG 0x1E
#define CODEC_MAP_TONE_CTRL 0x1F
#define CODEC_MAP_MASTER_A_VOL 0x20
#define CODEC_MAP_MASTER_B_VOL 0x21
#define CODEC_MAP_HP_A_VOL 0x22
#define CODEC_MAP_HP_B_VOL 0x23
#define CODEC_MAP_SPEAK_A_VOL 0x24
#define CODEC_MAP_SPEAK_B_VOL 0x25
#define CODEC_MAP_CH_MIX_SWAP 0x26
#define CODEC_MAP_LIMIT_CTRL1 0x27
Page 11
#define CODEC_MAP_LIMIT_CTRL2 0x28
#define CODEC_MAP_LIMIT_ATTACK 0x29
#define CODEC_MAP_OVFL_CLK_STATUS 0x2E
#define CODEC_MAP_BATT_COMP 0x2F
#define CODEC_MAP_VP_BATT_LEVEL 0x30
#define CODEC_MAP_SPEAK_STATUS 0x31
#define CODEC_MAP_CHARGE_PUMP_FREQ 0x34
#define CODEC_MAPBYTE_INC 0x80
#define CODEC_I2C_ADDRESS 0x94 // Define uC I2C audio codec address.
void codec_init(void); // Initialize the audio codec.
void codec_send_ctrl(uint8_t control_bytes[], // Send control bytes to the codec over I2C.
uint8_t num_bytes);
uint8_t codec_read_register(uint8_t map_byte); // Read from audio codec register.
void codec_ctrl_init(); // Initialize the audio codec map registers.
#endif
Page 12
CODEC.C
/*
* CS43L22 Codec Driver Implementation for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#include "codec.h"
void codec_init() {
RCC_APB1PeriphClockCmd(CODEC_SPI_CLK | CODEC_I2C_CLK, ENABLE); // Enable power clocks to I2C and SPI.
RCC_AHB1PeriphClockCmd(I2C_SCL_CLK | I2C_SDA_CLK |
CODEC_RESET_CLK | I2S_WS_CLK | I2S_SCLK_CLK |
I2S_SD_CLK | I2S_MCLK_CLK, ENABLE); // Enable GPIO power clocks
RCC_PLLI2SCmd(ENABLE); // Enable PLL on I2S.
GPIO_InitTypeDef I2C_SCL_SDA_TypeDef; // Initialize I2C pins.
I2C_SCL_SDA_TypeDef.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
I2C_SCL_SDA_TypeDef.GPIO_Mode = GPIO_Mode_AF;
I2C_SCL_SDA_TypeDef.GPIO_OType = GPIO_OType_OD;
I2C_SCL_SDA_TypeDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
I2C_SCL_SDA_TypeDef.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2C_SCL_GPIO_PORT, &I2C_SCL_SDA_TypeDef);
GPIO_PinAFConfig(I2C_SCL_GPIO_PORT, I2C_SCL_PINSOURCE, CODEC_I2C_AF); // Map Alternate Function pins to I2C1.
GPIO_PinAFConfig(I2C_SCL_GPIO_PORT, I2C_SDA_PINSOURCE, CODEC_I2C_AF);
GPIO_InitTypeDef CODEC_RESET_TypeDef; // Initialize audio codec reset pin.
CODEC_RESET_TypeDef.GPIO_Pin = CODEC_RESET_PIN;
CODEC_RESET_TypeDef.GPIO_Mode = GPIO_Mode_OUT;
CODEC_RESET_TypeDef.GPIO_OType = GPIO_OType_PP;
CODEC_RESET_TypeDef.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(CODEC_RESET_GPIO_PORT, &CODEC_RESET_TypeDef);
GPIO_InitTypeDef I2S_WS_TypeDef; // Initialize I2S Pins.
I2S_WS_TypeDef.GPIO_Pin = I2S_WS_PIN;
I2S_WS_TypeDef.GPIO_Mode = GPIO_Mode_AF;
I2S_WS_TypeDef.GPIO_OType = GPIO_OType_PP;
I2S_WS_TypeDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
I2S_WS_TypeDef.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2S_WS_GPIO_PORT, &I2S_WS_TypeDef);
GPIO_PinAFConfig(I2S_WS_GPIO_PORT, I2S_WS_PINSOURCE, CODEC_SPI_AF); // Map pin to SPI3 for I2S use.
GPIO_InitTypeDef I2S_SCLK_SD_MCLK_TypeDef; // Initialize I2S Pins.
I2S_SCLK_SD_MCLK_TypeDef.GPIO_Pin = I2S_SCLK_PIN | I2S_SD_PIN | I2S_MCLK_PIN;
I2S_SCLK_SD_MCLK_TypeDef.GPIO_Mode = GPIO_Mode_AF;
I2S_SCLK_SD_MCLK_TypeDef.GPIO_OType = GPIO_OType_PP;
I2S_SCLK_SD_MCLK_TypeDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
I2S_SCLK_SD_MCLK_TypeDef.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2S_SCLK_GPIO_PORT, &I2S_SCLK_SD_MCLK_TypeDef);
GPIO_PinAFConfig(I2S_SCLK_GPIO_PORT, I2S_SCLK_PINSOURCE, CODEC_SPI_AF); // Map Pins to SPI3 for I2S use.
GPIO_PinAFConfig(I2S_SCLK_GPIO_PORT, I2S_SD_PINSOURCE, CODEC_SPI_AF);
GPIO_PinAFConfig(I2S_SCLK_GPIO_PORT, I2S_MCLK_PINSOURCE, CODEC_SPI_AF);
GPIO_ResetBits(CODEC_RESET_GPIO_PORT, CODEC_RESET_PIN); // Keep audio codec off.
SPI_I2S_DeInit(CODEC_SPI);
I2S_InitTypeDef I2Sx_TypeDef; // Initialize SPI I2S.
I2Sx_TypeDef.I2S_AudioFreq = I2S_AudioFreq_48k;
I2Sx_TypeDef.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
I2Sx_TypeDef.I2S_Mode = I2S_Mode_MasterTx;
I2Sx_TypeDef.I2S_Standard = I2S_Standard_Phillips;
I2Sx_TypeDef.I2S_DataFormat = I2S_DataFormat_16b;
I2Sx_TypeDef.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(CODEC_SPI, &I2Sx_TypeDef);
I2C_DeInit(CODEC_I2C);
I2C_InitTypeDef I2Cx_TypeDef; // Initialize I2C.
I2Cx_TypeDef.I2C_ClockSpeed = 100000;
I2Cx_TypeDef.I2C_Mode = I2C_Mode_I2C;
I2Cx_TypeDef.I2C_OwnAddress1 = 99;
I2Cx_TypeDef.I2C_Ack = I2C_Ack_Enable;
I2Cx_TypeDef.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2Cx_TypeDef.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_Init(CODEC_I2C, &I2Cx_TypeDef);
I2C_Cmd(CODEC_I2C, ENABLE);
}
void codec_ctrl_init()
{
uint32_t delay_count = 1000000;
uint8_t codec_cmd_buffer[5]; // Buff stores list of commands to send codec.
Page 13
uint8_t reg_value = 0xFF;
GPIO_SetBits(CODEC_RESET_GPIO_PORT, CODEC_RESET_PIN);
while (delay_count > 0)
{
delay_count--;
}
codec_cmd_buffer[0] = CODEC_MAP_PLAYBACK_CTRL1; // Keep audio codec OFF.
codec_cmd_buffer[1] = 0x01;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x00; // Begin initialization sequence.
codec_cmd_buffer[1] = 0x99;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x47;
codec_cmd_buffer[1] = 0x80;
codec_send_ctrl(codec_cmd_buffer, 2);
reg_value = codec_read_register(0x32);
codec_cmd_buffer[0] = 0x32;
codec_cmd_buffer[1] = reg_value | 0x80;
codec_send_ctrl(codec_cmd_buffer, 2);
reg_value = codec_read_register(0x32);
codec_cmd_buffer[0] = 0x32;
codec_cmd_buffer[1] = reg_value & (~0x80);
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x00;
codec_cmd_buffer[1] = 0x00;
codec_send_ctrl(codec_cmd_buffer, 2); // Initialization sequence complete.
codec_cmd_buffer[0] = CODEC_MAP_PWR_CTRL2;
codec_cmd_buffer[1] = 0xAF;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = CODEC_MAP_PLAYBACK_CTRL1;
codec_cmd_buffer[1] = 0x70;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = CODEC_MAP_CLK_CTRL;
codec_cmd_buffer[1] = 0x81; // Auto detect clock.
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = CODEC_MAP_IF_CTRL1;
codec_cmd_buffer[1] = 0x07;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x0A; // Analog ZC SR settings
codec_cmd_buffer[1] = 0x00;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x27; // Limit Ctl 1 thresholds
codec_cmd_buffer[1] = 0x00;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = 0x1A | CODEC_MAPBYTE_INC;
codec_cmd_buffer[1] = 0x0A;
codec_cmd_buffer[2] = 0x0A;
codec_send_ctrl(codec_cmd_buffer, 3);
codec_cmd_buffer[0] = 0x1F; // Tone ctl
codec_cmd_buffer[1] = 0x0F;
codec_send_ctrl(codec_cmd_buffer, 2);
codec_cmd_buffer[0] = CODEC_MAP_PWR_CTRL1;
codec_cmd_buffer[1] = 0x9E;
codec_send_ctrl(codec_cmd_buffer, 2);
}
void codec_send_ctrl(uint8_t control_bytes[], uint8_t num_bytes)
{
uint8_t bytes_sent=0;
while (I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY)) {}
I2C_GenerateSTART(CODEC_I2C, ENABLE);
while (!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_SB)) {}
I2C_Send7bitAddress(CODEC_I2C, CODEC_I2C_ADDRESS, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}
while (bytes_sent < num_bytes)
{
I2C_SendData(CODEC_I2C, control_bytes[bytes_sent]);
bytes_sent++;
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING)) {}
}
while(!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BTF)) {}
I2C_GenerateSTOP(CODEC_I2C, ENABLE);
}
Page 14
uint8_t codec_read_register(uint8_t map_byte)
{
uint8_t received_byte = 0;
while (I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY)) {}
I2C_GenerateSTART(CODEC_I2C, ENABLE);
while (!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_SB)) {}
I2C_Send7bitAddress(CODEC_I2C, CODEC_I2C_ADDRESS, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {}
I2C_SendData(CODEC_I2C, map_byte); // Sets the transmitter address.
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING)) {}
I2C_GenerateSTOP(CODEC_I2C, ENABLE);
while (I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_BUSY)) {}
I2C_AcknowledgeConfig(CODEC_I2C, DISABLE);
I2C_GenerateSTART(CODEC_I2C, ENABLE);
while (!I2C_GetFlagStatus(CODEC_I2C, I2C_FLAG_SB)) {}
I2C_Send7bitAddress(CODEC_I2C, CODEC_I2C_ADDRESS, I2C_Direction_Receiver);
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {}
while (!I2C_CheckEvent(CODEC_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)) {}
received_byte = I2C_ReceiveData(CODEC_I2C);
I2C_GenerateSTOP(CODEC_I2C, ENABLE);
return received_byte;
}
Page 15
AUDIO.H
/*
* Audio DMA Interface Header File for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#ifndef AUDIO_H_
#define AUDIO_H_
#include "stm32f4xx.h"
#include "math.h"
#include "guitar_gen.h"
#define BUFFER_SIZE 256 // 256 halfwords in each audio buffer.
void audio_init(); // Initialize the audio system for DMA.
void audio_clear_buffers(); // Clear the audio buffers.
#endif
Page 16
AUDIO.C
/*
* Audio DMA Interface Implementation for STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#include "audio.h"
int16_t double_buffer_1[BUFFER_SIZE]; // The first DMA audio buffer. (ping-pong buffering)
int16_t double_buffer_2[BUFFER_SIZE]; // The second DMA audio buffer. (ping-pong buffering)
void DMA1_Stream7_IRQHandler() { // Called when audio buffer transfer is complete.
DMA_ClearITPendingBit(DMA1_Stream7, DMA_IT_TCIF7); // Clear the interrupt bit so the DMA transmission isn't jammed
if (DMA_GetCurrentMemoryTarget(DMA1_Stream7) == DMA_Memory_0) { // If currently reading audio buff 1 then write to audio buff 2.
guitar_gen_update_buffers(double_buffer_2);
} else {
guitar_gen_update_buffers(double_buffer_1);
}
}
void audio_init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // Enable the DMA clock.
I2S_Cmd(SPI3, ENABLE); // Enable I2S on SPI3.
DMA_InitTypeDef DMA1_TypeDef;
DMA1_TypeDef.DMA_BufferSize = BUFFER_SIZE;
DMA1_TypeDef.DMA_Channel = DMA_Channel_0;
DMA1_TypeDef.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA1_TypeDef.DMA_Mode = DMA_Mode_Circular; // DMA continually loops through audio buffers.
DMA1_TypeDef.DMA_FIFOMode = DMA_FIFOMode_Disable; // Don't need FIFO mode.
DMA1_TypeDef.DMA_Memory0BaseAddr = (uint32_t) &double_buffer_1; // Set first buffer to audio buffer 1.
DMA1_TypeDef.DMA_MemoryBurst = DMA_MemoryBurst_Single; // Don't want burst reads from memory.
DMA1_TypeDef.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 bit signed int is used in buffers.
DMA1_TypeDef.DMA_MemoryInc = DMA_MemoryInc_Enable; // Enable increment through memory. (inc thru audio buffer)
DMA1_TypeDef.DMA_PeripheralBaseAddr = 0x40003C0C;
DMA1_TypeDef.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA1_TypeDef.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // Don't want burst writes to peripheral.
DMA1_TypeDef.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Don't want peripheral to incr counter.
DMA1_TypeDef.DMA_Priority = DMA_Priority_High; // High priority as must be frequently
DMA_Init(DMA1_Stream7, &DMA1_TypeDef); // updated for sound to be distortion-free.
DMA_ITConfig(DMA1_Stream7, DMA_IT_TC, ENABLE); // Enable the DMA transfer complete interrupt.
NVIC_InitTypeDef NVIC_DMA1_TypeDef;
NVIC_DMA1_TypeDef.NVIC_IRQChannel = DMA1_Stream7_IRQn; // Map the interrupt to the NVIC.
NVIC_DMA1_TypeDef.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_DMA1_TypeDef.NVIC_IRQChannelSubPriority = 0;
NVIC_DMA1_TypeDef.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_DMA1_TypeDef);
DMA_DoubleBufferModeConfig(DMA1_Stream7, // Set second buffer to audio buffer 2 and set default buffer to buffer 1.
(uint32_t) &double_buffer_2, DMA_Memory_0);
DMA_DoubleBufferModeCmd(DMA1_Stream7, ENABLE); // Enable double buffering.
audio_clear_buffers();
DMA_Cmd(DMA1_Stream7, ENABLE); // Enable DMA.
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE); // Enable I2S transmission over DMA.
}
void audio_clear_buffers() {
int i;
for (i=0; i<BUFFER_SIZE; i++) {
double_buffer_1[i] = 0;
double_buffer_2[i] = 0;
}
}
Page 17
MAIN.C
/*
* Demo of Karplus-Strong algorithm implementation on STM32F4 Discovery
* Andrew Kozik Embedded Systems Project 2015 (UCT)
*/
#include "stm32f4xx.h"
#include "stm32f4_discovery.h"
#include "lcd.h"
#include "codec.h"
#include "audio.h"
#include "guitar_gen.h"
void delay(int time) {
int i;
for (i = 0; i < 5*time; i++) {}
}
int main(void) {
lcd_init();
codec_init();
codec_ctrl_init();
audio_init();
guitar_gen_init();
const char * string = "Embedded Project\n(C) 2015";
lcd_put_string(string);
while (1) {
guitar_gen_strum(A, 0); // Play the bass-line to Hysteria by Muse
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(F, 1);
delay (200000);
guitar_gen_strum(F, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
Page 18
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(E, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(C, 2);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(C, 2);
delay (200000);
guitar_gen_strum(D, 2);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(C, 2);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(C, 2);
delay (200000);
guitar_gen_strum(B, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(B, 1);
delay (200000);
guitar_gen_strum(As, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(As, 1);
delay (200000);
guitar_gen_strum(A, 1);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(G, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(A, 0);
delay (200000);
Page 19
guitar_gen_strum(F, 1);
delay (200000);
guitar_gen_strum(F, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
guitar_gen_strum(D, 1);
delay (200000);
guitar_gen_strum(E, 1);
delay (200000);
}
}