back to www.audiodesignguide.com |
To get more information contact me at: webmaster@audiodesignguide.com |
DAC and Volume control with LCD and
Touch Screen
April 2024 |
INTRODUCTION
All the modern DAC, ADC and Digital Volume chips need
to be configurated and managed using a microcontroller connected using I2C or SPI.
Here I would like to give you the elements to
create this control module without software or hardware problems and
with no expensive components.
Before starting I have to give a special
thanks to the STM community because
they have helped me several times to solve problems and I reciprocated by
adding useful content .
STM32 MODULES
Many years ago I started
developing DAC and Volume controls using 8 bit PIC microcontroller by
MIcrochip but in the last years a
new line of microcontroller by STM32 give
more performances, integrated evaluation module with lcd and a free develop
environment.
Below I insert a series of modules that I used to create
prototypes and products in production for some companies.
For all has
been used the USB
ST-LINK/V2 in-circuit debugger and
programmer in some case included in the module.
STM32F746G-DISCO
- STM32F746NGH6 Arm® Cortex® core-based microcontroller frequency of up to 216 MHz with 1 Mbyte of Flash memory and 340 Kbytes of RAM - 4.3” RGB 480×272 color LCD-TFT - Capacitive touch screen - Ethernet compliant with IEEE-802.3-2002 - USB OTG HS - USB OTG FS - SAI audio codec - Two ST-MEMS digital microphones - 128-Mbit Quad-SPI Flash memory - 128-Mbit SDRAM (64 Mbits accessible) - Two user and reset push-buttons - MicroSD card - On-board ST-LINK/V2 schematic |
STM32F469I-DISCOVERY
- STM32F469NIH6 Arm® 32-bit Cortex®-M4 microcontroller frequency of up to 180 MHz with 2 Mbytes of Flash memory and 324 Kbytes of RAM - 4" inches 800x480 pixel - LCD with MIPI DSI® interface - Capacitive touch screen - USB OTG FS - SAI Audio DAC - 128-Mbit Quad-SPI NOR Flash - 128-Mbit SDRAM - Two user and reset push-buttons - MicroSD card - On-board ST-LINK/V2 schematic |
STM32F429BIT6 module + 5" or 7" LCD - STM32F429BIT6 Arm® 32-bit Cortex®-M4 CPU frequency up to 180 MHz with 2 MB of Flash memory and Up to 256+4 KB of SRAM - 5" or 7" LCD 800x480 connnected in RGB - LCD parallel interface - Capacitive touch screen - 128-Mbit SDRAM - Two user and reset push-buttons - MicroSD card - External ST-LINK/V2 is necessary to flash Alixpress web shop |
Click on STM32 module name to dowload the
user manual. Click on microprocessor name to download the datasheet of the chip. |
||
STM32F0DISCOVERY + 4x20 char LCD - STM32F051R8T6 microcontroller featuring 64 KB Flash memory, 8 KB RAM frequency up to 48 MHz - Two push buttons (user and reset) - On-board ST-LINK/V2 schematic |
ST-Link V2 Mini USB |
Only the STM32F429BIT6 module need an external ST-Link V2 Mini USB to flash the code.
All these modules can be powered by USB during the develop phase and with an
external power supply for the final use.
Here follows two photos to create the right
connection from ST-LINK/V2 and the
STM32F429BIT6 module (keep attention).
You can flash the STM32 microprocessor directly with the
CubeIDE development platform Run button or with
ST-LINK
Utility.
In this case is necessary after the compiler steps is necessary
produce the .hex file (see the last section of this page).
DEVELOPMENT PLATFORM
If you are using a STM32 DISCOVERY module is possibile generate an empty project selecting the evaulation board.
There is also a function to generate the code for pin initialization in a visual mode but I don't like to use this because the automatically generated code is mixed with the manually entered code and if you don't follow the rules some changes could be lost.
MICROPROCESSOR I/O PINS
The microprocessors have many I/O pins but there are many
differences between one and the other.
Almost all can be configured as
simple inputs or outputs to connect buttons or relays but only a specific set
can be configured for SPI, I2C.
Also for the encoders the correct pair of pins must be
identified to enable the specific function with timer.
So the first activity
to define the connections is to identify the SPI, I2C, ADC and Timer (TIM) for encoder pins.
All of the presented STM32 modules allow access to allows access only to certain
pins via dip connectors.
For the STM32F746-DISCO and the STM32F469-DISCOVERY
you can buy the
PCB Board per Arduino UNO R3 Shield Board to connect the pins.
Here the develop environment to test the STM32F429BIT6 module with 7" LCD.
I2C and SPI PROTOCOLS
The I2C and the SPI are serial communication protocols to allow communication between the microprocessor and other chips such as DAC, ADC and Volme Control.
There are many documents about these two protocols but this on eVision Systems is a good info.
I2C (Inter-Integrated Circuit) is an on-board
communication protocol, which is ideal for short distances and low
bandwidth. |
|
The Serial Peripheral Interface
(SPI) is another serial communication protocol that is heavily used in
embedded systems. Like I2C, it is also has a master-slave architecture but it is a 4+ wire bus. SPI requires a clock line (SCK), two data lines for transmitting data bidirectionally known as the MOSI and MISO lines. Additionally, there must be a slave select (SS or CS) line for each slave on the bus. Instead of utilizing an addressing system like I2C, multiple slaves are controlled by the master via the slave select lines. |
PERIPHERICALS
This microprocessors will be connected to
varios periphericals:
The Infrared receiver should operate with 3.3VDC
power supply, the same of STM32. |
||
DVCC is 5V so min Input high volage is 5 * 0.7 = 3.5V.
DVCC is 5V but min Input high volage is 2V so able to be connected to 3.3V STM32 SPI pins.
START WITH A STM32 DISCOVERY BOARD
As told before you can create an empty project with Cube
IDE with few clicks
Select File - New - STM32 Project, on the following
panel select Board Selector and search the model
Here select No
Now modify the main() with these lines of code to blick the blue led.
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_GPIO_WritePin(LED4_GPIO_Port,
LED4_Pin, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(LED4_GPIO_Port,
LED4_Pin, GPIO_PIN_SET);
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE
END 3 */
START WITH A SIMPLE CODE
Here a simple code to switch on the on-board LED with
STM32F746-DISCO.
First identify a pin on STM32 module searching on user
manual, D2 is a valid choice because have no timer, no I2C and no SPI.
D2 on ARDUINO connectors is connected to PG6 so port GPIOG and pin GPIO_PIN_6.
So connect a LED in series with a 680ohm resistor to this
pin and use this code.
void main()
{
HAL_Init();
SystemClock_Config();
GPIO_InitTypeDef
gpio_init_structure;
__HAL_RCC_GPIOG_CLK_ENABLE();
// <--- enable port GPIOG
gpio_init_structure.Pin
= GPIO_PIN_6;
gpio_init_structure.Mode =
GPIO_MODE_OUTPUT_PP;
gpio_init_structure.Pull =
GPIO_NOPULL; // <--- no pull-up
is necessary
gpio_init_structure.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(GPIOG, &gpio_init_structure);
// <--- init port GPIOG
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, GPIO_PIN_RESET); // <--- RESET mean low level so 0V
HAL_GPIO_WritePin(GPIOH,
GPIO_PIN_6, GPIO_PIN_SET); // <--- SET mean high level so 3.3V
}
Here my code to use encoder with STM32 modules
First search on datasheet 2 pins with the same timer, for example TIM8_CH1 and
TIM8_CH2.
In my case was a STM32F469 and I have used PC6 and PC7 pin
associated to timer 8 (TIM8).
After search in the file
stm32f4xx_hal_gpio_ex.h the relative AFx for your TIMx.
#define GPIO_AF3_TIM8 ((uint8_t)0x03) /* TIM8
Alternate Function mapping */
In the main.c insert the following code
TIM_HandleTypeDef htim8;
uint32_t TIM8_save_counter = 0;
void MX_TIM8_Init(void)
{
TIM_Encoder_InitTypeDef sConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
GPIO_InitTypeDef gpio_init_structure;
__HAL_RCC_GPIOC_CLK_ENABLE();
__TIM8_CLK_ENABLE();
gpio_init_structure.Pin = GPIO_PIN_6|GPIO_PIN_7;
gpio_init_structure.Mode = GPIO_MODE_AF_PP;
gpio_init_structure.Pull = GPIO_PULLUP;
gpio_init_structure.Speed = GPIO_SPEED_LOW;
gpio_init_structure.Alternate = GPIO_AF3_TIM8;
HAL_GPIO_Init(GPIOC, &gpio_init_structure);
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 65535;
htim8.Init.ClockDivision =
TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
sConfig.IC1Filter =
10;
sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
sConfig.IC2Filter =
0;
if (HAL_TIM_Encoder_Init(&htim8, &sConfig) !=
HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8,
&sMasterConfig) != HAL_OK)
{
Error_Handler();
}
//HAL_TIM_Encoder_Start(&htim8, TIM_CHANNEL_ALL);
HAL_TIM_Encoder_Start(&htim8, TIM_CHANNEL_1|TIM_CHANNEL_2);
}
and in the main loop insert this code to check the rotation
if (TIM8->CNT != TIM8_save_counter)
{
if
(TIM8_save_counter == 0 && TIM8->CNT == 65535)
buttonInUp();
else if (TIM8_save_counter == 65535 &&
TIM8->CNT == 0)
buttonInDown();
else if (TIM8_save_counter < TIM8->CNT)
buttonInDown();
else
buttonInUp();
TIM8_save_counter = TIM8->CNT;
}
INFRARED
It is possible to use this very cheap infrared remote with the SC6122 chip.
This remote control utilizes NEC IR protocol for
encoding.
In the following example has been used the pin PA15 (GPIOA
- GPIO_PIN_15) and has been enabled the interrupt
EXTI15_10_IRQn
on the transition from a high level to a low level
(GPIO_MODE_IT_FALLING).
#define IR_ADDR (0x20)
#define IR_ON (0x1C)
#define IR_UP (0x10)
#define
IR_DOWN (0x1D)
#define IR_MUTE (0x11)
#define IR_FWR (0x15)
#define IR_REW (0x18)
#define IR_SET (0x19)
#define IR_STD
(0x25)
if ((IR_addr1 == IR_ADDR &&
IR_addr2 == IR_ADDR) ||
(IR_addr1 == IR2_ADDR1 && IR_addr2 ==
IR2_ADDR2))
{
IR_addr1 = IR_addr2 = 0;
switch(IR_data1)
{
case IR_UP:
case IR2_UP:
buttonUpClicked();
break;
case IR_DOWN:
case IR2_DOWN:
buttonDownClicked();
break;
}
void EXTI15_10_IRQHandler(void)
{
#define MAX_NEG_START (10000)
#define MIN_NEG_START (8000)
#define MAX_POS_START (5000)
#define MIN_POS_START (3500)
#define MAX_POS_REP (2500)
#define MIN_POS_REP (1500)
#define MIN_POS_BIT1 (1200)
#define DELAY_STEP (250)
int l, k;
unsigned char data1, data2, addr1,
addr2;
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
for (k = 0;
k < MAX_NEG_START && !HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15); k += DELAY_STEP)
{
delayUs(DELAY_STEP);
}
if ((k <
MAX_NEG_START) && (k > MIN_NEG_START))
{
for (l = 0;
l < MAX_POS_START && HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15); l += DELAY_STEP)
{
delayUs(DELAY_STEP);
}
if (l <
MAX_POS_REP && l > MIN_POS_REP)
{
while(!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15));
if (save_addr1 > 0)
{
IR_addr1 = save_addr1;
IR_addr2 = save_addr2;
}
else
{
IR_addr1 = 0;
IR_addr2 = 0;
}
return;
}
if ((l < MAX_POS_START) && (l > MIN_POS_START))
{
while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15));
addr1 = IR_readByte(0, MIN_POS_BIT1);
addr2 = IR_readByte(1, MIN_POS_BIT1);
data1 = IR_readByte(0, MIN_POS_BIT1);
data2 = IR_readByte(1, MIN_POS_BIT1);
#if OLD_REMOTE
if (addr1 != addr2) return;
#endif
if (data1 != data2) return;
if (addr1 == 0) return;
if (data1 == 0) return;
save_addr1 = addr1;
save_addr2 = addr2;
IR_addr1 = addr1;
IR_addr2 = addr2;
IR_data1 = data1;
IR_data2 = data2;
}
}
}
unsigned char IR_readByte(int
inv, int CheckTime)
{
unsigned char out, tmp[10];
int i, k;
for (k = 0;
k < 8; k++)
{
for (i = 0; HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15); i += DELAY_STEP)
{
delayUs(DELAY_STEP);
}
while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15));
if (i > CheckTime)
{
if (inv == 0 )
tmp[k] = '1';
else
tmp[k] = '0';
}
else
{
if (inv == 0 )
tmp[k] = '0';
else
tmp[k] = '1';
}
}
out
= 0;
for (k = 7; k >= 0; k--)
{
if (tmp[k]
== '1')
{
out <<= 1; out |= 1;
}
else
{
out <<= 1;
}
}
return out;
}
void
BSP_IR_Init()
{
GPIO_InitTypeDef
gpio_init_structure;
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_structure.Pin = GPIO_PIN_15;
gpio_init_structure.Mode =
GPIO_MODE_IT_FALLING;
gpio_init_structure.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &gpio_init_structure);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
FRAM TO STORE CONFIGURATION
If you want to save some variables of configuration like volume level, balance and input selected also if the power is disconnected a solution is to add an external I2C FRAM chip.
I have tested the MB85RC256V availabe also in DIP module on Alixpress
Insert in the main.c these following lines to use the I2S functions:
extern void
EEPROM_IO_Init(void);
extern HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t
DevAddress, uint16_t MemAddress, uint8_t* pBuffer, uint32_t BufferSize);
extern HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress, uint16_t
MemAddress, uint8_t* pBuffer, uint32_t BufferSize);
extern HAL_StatusTypeDef
EEPROM_IO_IsDeviceReady (uint16_t DevAddress, uint32_t Trials);
typedef
struct saves_tag_conf {
unsigned char not_first;
unsigned char inputsel;
int level;
int balance;
} saved_conf_str;
saved_conf_str saved_conf; // = { 0, 0,
32, 0 };
Insert in the main() the init function:
EEPROM_IO_Init();
The byte 0b10100000 used as first parameter in the call to the functions is the I2C device address so in my case A0,A1 and A2 are connected to ground.
void
Read_Saved_Configuration()
{
uint8_t ret;
if ((ret = EEPROM_IO_IsDeviceReady(0b10100000, 10)) != 0x00)
{
return;
}
if ((ret = EEPROM_IO_ReadData(0b10100001, 0x1,
(uint8_t *)&saved_conf, sizeof(saved_conf))) != 0x00)
{
return;
}
if (saved_conf.not_first != 0)
{
inputsel =
saved_conf.inputsel;
level
= saved_conf.level;
balance
= saved_conf.balance;
}
saved_conf.not_first = 1;
Write_Saved_Configuration();
}
void Write_Saved_Configuration()
{
uint8_t ret;
saved_conf.inputsel = inputsel;
saved_conf.level = level;
saved_conf.balance = balance;
if ((ret = EEPROM_IO_IsDeviceReady(0b10100000, 10)) !=
0x00)
{
return;
}
if ((ret =
EEPROM_IO_WriteData(0b10100000, 0x1, (uint8_t *)&saved_conf,
sizeof(saved_conf))) != 0x00)
{
return;
}
}
LCD IMAGE CONVERTER
In order to display images on the graphic LCD modules is necessary transform this from standard file bmp,gig, jpeg and png to “C” source format for embedded applications.
I am using this LCD Image Converter to configure in the following mode.
load image
configure parameters
here download template file
select convert
select the output file
The output file is an include to add in the STM32CubeIDE project.
The images get a big amount of memory and this may be too
large for the available flash space so a solution is store the images in an
external Serial Flash memory (QSPI), in same case included in the STM32 module.
A single full screen image for a 800 x 480 pixel LCD get 750K bytes and this
is the result of 800 x 480 x 2 / 1024 = 750.
Some of STM32 discovery board have an integrated Quad SPI
Flash Memory chip
32F746GDISCOVERY
N25Q128A
128Mb 108 MHz SPI-compatible serial bus interface
32F469IDISCOVERY MT25QL128ABA1EW9
128Mb 166 MHz SPI-compatible serial bus interface
It is possible store static images in this chip during the flash to have a
larger space for the program.
Here the logo.h file generated by LCD converter from the
logo.png image.
#ifndef logo_H_
#define
logo_H_
#include <stdint.h>
// struct packing, pragma for GCC !!!
#pragma pack(push, 1)
typedef struct logo_tagBITMAPFILEHEADER {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
} logo_BITMAPFILEHEADER; // size is 14 bytes
typedef struct logo_tagBITMAPINFOHEADER {
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
}
logo_BITMAPINFOHEADER; // size is 40 bytes
typedef struct logo_tag_Struct
{
// offset 0, size 14
logo_BITMAPFILEHEADER fileHeader;
// offset 14, size 40
logo_BITMAPINFOHEADER infoHeader;
// offset 54, size 51600
words
uint16_t data[51600];
} logo_Struct;
const
logo_Struct logo = {
{
0x4d42u,
sizeof(logo_BITMAPINFOHEADER) + sizeof(logo_BITMAPFILEHEADER) + (51600 * 2),
0x0000u,
0x0000u,
sizeof(logo_BITMAPINFOHEADER) + sizeof(logo_BITMAPFILEHEADER)
},
{
sizeof(logo_BITMAPINFOHEADER),
400,
129,
1u,
16,
0x00000003u,
(51600 * 2),
0x00000000ul,
0x00000000ul,
0x00000000ul,
0x00000000ul
},
{
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, .....
}
};
// struct
packing, pragma for GCC !!!
#pragma pack (pop)
#endif /* settings1_H_
*/
Here the logo.h file modified by hand to use the QSPI
//#ifndef logo_H_
//#define logo_H_
#include <stdint.h>
// struct packing,
pragma for GCC !!!
#pragma pack(push, 1)
typedef struct
logo_tagBITMAPFILEHEADER {
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
}
logo_BITMAPFILEHEADER; // size is 14 bytes
typedef struct
logo_tagBITMAPINFOHEADER {
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
}
logo_BITMAPINFOHEADER; // size is 40 bytes
typedef struct logo_tag_Struct
{
// offset 0, size 14
logo_BITMAPFILEHEADER fileHeader;
// offset 14, size 40
logo_BITMAPINFOHEADER infoHeader;
// offset 54, size 51600
words
uint16_t data[51600];
} logo_Struct;
#ifndef IMG_NO_DATA
#if
defined ( __ICCARM__ )
#pragma location = ".textqspi"
#else
__attribute__((section(".textqspi")))
#endif
const logo_Struct
logo = {
{
0x4d42u,
sizeof(logo_BITMAPINFOHEADER) + sizeof(logo_BITMAPFILEHEADER) + (51600 * 2),
0x0000u,
0x0000u,
sizeof(logo_BITMAPINFOHEADER) + sizeof(logo_BITMAPFILEHEADER)
},
{
sizeof(logo_BITMAPINFOHEADER),
400,
129,
1u,
16,
0x00000003u,
(51600 * 2),
0x00000000ul,
0x00000000ul,
0x00000000ul,
0x00000000ul
},
{
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, .....
}
};
#else
extern const logo_Struct logo;
#endif
// struct packing, pragma for GCC !!!
#pragma
pack (pop)
//#endif
create in the project a new
main_image.c file with only these lines
#include "logo.h"
void dummy_init (void);
void dummy_init (void)
{
}
in your main.c use these lines to init the QSPI
#define IMG_NO_DATA
#include "logo.h"
QSPI_HandleTypeDef hqspi;
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 255;
hqspi.Init.FifoThreshold = 1;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
hqspi.Init.FlashSize =
1;
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID =
QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if
(HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
int status = BSP_QSPI_Init();
if (status == QSPI_NOT_SUPPORTED)
{
BSP_LCD_DisplayStringAt(20, 10, (uint8_t*)"QSPI
Initialization : FAILED.", LEFT_MODE);
}
else if (status == QSPI_ERROR)
{
BSP_LCD_DisplayStringAt(20, 10, (uint8_t*)"QSPI
Initialization : FAILED.", LEFT_MODE);
}
else
{
BSP_LCD_DisplayStringAt(20, 10, (uint8_t*)"QSPI Initialization : OK.",
LEFT_MODE);
}
BSP_QSPI_MemoryMappedMode();
to display an image use:
BSP_LCD_DrawBitmap(1, 1, (uint8_t *)(&logo));
modify the file
STM32F746NGHx_FLASH.ld in the CubeWorkspace
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (xrw)
: ORIGIN = 0x20000000, LENGTH = 307K
QSPI (xrw) : ORIGIN =
0x90000000, LENGTH = 16M <<<<<<<< add this
Memory_B1(xrw)
: ORIGIN = 0x2004C000, LENGTH = 0x80
Memory_B2(xrw) :
ORIGIN = 0x2004C080, LENGTH = 0x80
Memory_B3(xrw) : ORIGIN
= 0x2004C100, LENGTH = 0x17d0
Memory_B4(xrw) : ORIGIN =
0x2004D8D0, LENGTH = 0x17d0
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
/* Startup code */
. = ALIGN(4);
} >FLASH
.textqspi :
<<< add this section
{
. = ALIGN(4);
_qspi_start = .; /*
create a global symbol at qspi start */
*(.textqspi) /* .textqspi sections */
*(.textqspi*) /* .textqspi* sections */
. = ALIGN(4);
_qspi_end = .; /*
define a global symbols at end of textqspi */
} >QSPI
...................
.ARM.attributes 0 :
{ *(.ARM.attributes) }
.textqspi : { *(.textqspi) } >QSPI
<<< add this
.RxDecripSection : {
*(.RxDescripSection) } >Memory_B1
.TxDescripSection : {
*(.TxDescripSection) } >Memory_B2
.RxarraySection : {
*(.RxBUF) } >Memory_B3
.TxarraySection : { *(.TxBUF) }
>Memory_B4
}
Now after Build project you should see a new section the the Build Analyzer, if you dont't see lines press Refresh .
If
you are using the Cube IDE application to flash use with this set
If you are using the ST-LINK application to flash use with this set
M-DAC VOLUME
Instead of use a
PGA2310,
MAS6116 or a MUSES72320 as Volume control is
possible to use a MDAC (Multiplying Digital-to-Analog Converter).
Some
companies like
Mark Levinson use the MDAC chips and the result is like the previous chips
but like
MAS6116 and MUSES72320 is necessary an
external op-amp.
Here the schematic found in the DAC8043 datasheet and
follows some link to articles about.
High-Resolution Multiplying DACs Excel at Handling AC Signals
Solid state R2R attenuator using analog switches or multiplying DAC
Precision, AC Reference Signal Attenuator Using the AD5546/AD5556 Multiplying DAC
The
Mark Levinson in some models use the
DAC8812 a MDAC with 16bit but also a 14 or 12 bits are enought to get a low
error near to 0.01dB.
I have tested the MDAC on STM32 module using this code
valid for DAC8812 with SPI interface.
#define VOL_MIN_MDAC (0)
#define VOL_MAX_MDAC (90)
float db_MDAC = 32.0;
case buttonUpClicked:
if (db_MDAC <
VOL_MAX_MDAC)
db_MDAC+=1.0;
case buttonDownClicked:
if (db_MDAC > VOL_MIN_MDAC)
db_MDAC-=1.0
if (balance == 0)
{
db_MDACL =
db_MDAC;
db_MDACR = db_MDAC;
}
float levelL16 = (65536.0 * pow(10.0, -db_MDACL / 20.0))
- 1;
float levelR16 = (65536.0 * pow(10.0, -db_MDACR / 20.0)) - 1;
BSP_SPI_Write_MDAC((uint16_t)levelL16, (uint16_t)levelR16);
void
BSP_SPI_Write_MDAC(uint16_t levelL, uint16_t levelR)
{
int addr_decode = 3;
int addr_decodeL = 1;
int addr_decodeR = 2;
HAL_GPIO_WritePin(GPIOA,
GPIO_PIN_8, GPIO_PIN_RESET); // CS pin
delayUs(10);
HAL_SPI_Transmit(&spi, (uint8_t * )&addr_decodeL, 1, HAL_MAX_DELAY);
uint16_t highByte = levelL >> 8;
uint16_t lowBytetemp =
levelL << 8;
uint16_t lowByte = lowBytetemp >> 8;
HAL_SPI_Transmit(&spi, (uint8_t * )&highByte, 1, HAL_MAX_DELAY);
HAL_SPI_Transmit(&spi, (uint8_t * )&lowByte, 1, HAL_MAX_DELAY);
delayUs(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8,
GPIO_PIN_SET); // CS pin
delayUs(200);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // CS pin
delayUs(10);
HAL_SPI_Transmit(&spi, (uint8_t *
)&addr_decodeR, 1, HAL_MAX_DELAY);
highByte = levelR
>> 8;
lowBytetemp = levelR << 8;
lowByte = lowBytetemp >> 8;
HAL_SPI_Transmit(&spi,
(uint8_t * )&highByte, 1, HAL_MAX_DELAY);
HAL_SPI_Transmit(&spi, (uint8_t * )&lowByte, 1, HAL_MAX_DELAY);
delayUs(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8,
GPIO_PIN_SET); // CS pin
}
CUBE IDE SETTINGS
To create the hex output file set this checkbox
or add this line
arm-none-eabi-objcopy -O ihex ${ProjName}.elf ${ProjName}.hex
To create the output files with the project name
To have the project name define inside the code
void Show_Version()
{
__attribute__((used))
static char compilation_date[] =
PROJECTNAME " " __DATE__ " " __TIME__;
BSP_LCD_SetFont (&Font24);
BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
BSP_LCD_SetBackColor(LCD_COLOR_TRANSPARENT);
BSP_LCD_DisplayStringAt(10, 420,
(uint8_t *)compilation_date, CENTER_MODE);
}
BACKLIGHT TRICKS
In many case the controller will be used with a stand-by operation so it is necessary switch-off the backlight.
For the STM32F469I-DISCOVERY to have a complete switch-off of the backlight you need to make a correction on STM32 module.
The 32F469IDISCOVERY Discovery board offers the option to control the EN pin by HW through port PA3. In such case, R117 must be removed and R119 soldered.
#define LCD_BL_CTRL_Pin GPIO_PIN_3
#define
LCD_BL_CTRL_GPIO_Port GPIOA
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin =
LCD_BL_CTRL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed =
GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LCD_BL_CTRL_GPIO_Port,
&GPIO_InitStruct);
HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_Port,
LCD_BL_CTRL_Pin, GPIO_PIN_RESET); /* backlight off */
HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_Port, LCD_BL_CTRL_Pin, GPIO_PIN_SET); /*
backlight on */