stm32f4 write/read 16bits into two 8-bit gpio port - stm32

I try to write 16 bit data into two selected 8-bits gpio ports . I must split data for
LSB and MSB :
void LCD_write_command(uint16_t cmd) {
GPIOD->ODR = cmd & 0x00ff; //lsb
GPIOA->ODR = (GPIOA->ODR & 0x00ff) | (cmd >> 8); //msb
}
and read data :
uint16_t LCD_read_data(void) {
(here is instruct gpio as input)
volatile uint16_t data = 0;
data = (uint16_t)GPIOD->IDR & 0x00ff; //lsb
data |= (uint16_t)GPIOA->IDR << 8 ; // msb
(here is instruct gpio as output)
return data;
}
When i use one 16bit gpio to write and read everything is fine:
void LCD_write_command(uint16_t cmd) {
GPIOD->ODR = cmd & 0xffff;
}
uint16_t LCD_read_data(void) {
volatile uint16_t data = 0;
data = (uint16_t)GPIOD->IDR & 0xffff;
return data;
}
I relay dont know what im missing.

wtite_bits(uint16_t cmd)
{
uint32_t data = GPIOA -> ODR;
data &= ~(0x1fff);
data |= cmd & 0x1fff;
GPIOA -> ODR = data;
data = GPIOB -> ODR;
data &= ~(0x0007);
data |= (cmd & 0x8fff) >> 13;
GPIOB -> ODR = data;
}
preserve other bits in the register

You need to learn a bit more about bitwise operations.
Writing
void LCD_write_command(uint16_t cmd) {
uint32_t tmp = GPIOD->ODR;
tmp &= ~(0xff);
tmp |= (cmd & 0x00ff);
GPIOD->ODR = tmp; //lsb
tmp = GPIOA->ODR;
tmp &= ~(0xff);
tmp |= (cmd >> 8);
GPIOA->ODR = tmp; //msb
}
or
void LCD_write_command(uint16_t cmd) {
*(volatile uint8_t *)&GPIOD->ODR = cmd & 0xff;
*(volatile uint8_t *)&GPIOA->ODR = cmd >> 8; //msb
}
forcing the compiler to use 8 bit store instructions.
Before using non word access to the registers check in the RM if your micro allows it:

Non of yours sugested code works for me , below is original source code of handling with LCD:
void LCD_write_command(uint16_t cmd) {
GPIOB->BRR = LCD_CS; // LCD_CS low (chip select pull)
GPIOB->BRR = LCD_RS; // LCD_RS low (register select = instruction)
//GPIOA->ODR = cmd; // put cmd to PortA (full length)
// put cmd [0..12] bits to PortA (actual LCD_DB00..LCD_DB12)
// put cmd [13..15] bits to PortB (actual LCD_DB13..LCD_DB15)
GPIOA->ODR = cmd & 0x1fff;
GPIOB->ODR = (GPIOB->ODR & 0xfff8) | (cmd >> 13);
GPIOB->BRR = LCD_WR; // pull LCD_WR to low (write strobe start)
// Write strobe 66ns long by datasheet. GPIO speed on STM32F103 at 72MHz slower -> delay is unnecessary
// asm volatile ("nop");
GPIOB->BSRR = LCD_WR; // pull LCD_WR to high (write strobe end)
GPIOB->BSRR = LCD_CS; // LCD_CS high (chip select release)
}
I've checked RM and i can operate on 8-bit ,half word and word.

I had similar problem: need to write some bits to port at same time.
My output bits are on port B: 0,1,2,4,5,6,7,8,9 (see missing bit 3).
It is possible to read whole port (ODR), and/or bits and write it back.
Faster version is to set all bits:
GPIOB ->BSRR =0b00000000000000000000001111111011;
and then clear only ones needed:
GPIOB ->BRR =some_bits_to_clear;
It was like some custom 74LS154 chip.
Or even try to clear and set bits at same time using:
GPIOB ->BSRR =bits_to clear<<16 | bits_to_set;
Depending on situation, there are many ways to optimize code.

Related

How to interact with the Linux PCA953X.c driver? How does one utilize this driver?

I have a PCA9535 GPIO Expander board connected through I²C to my Raspberry Pi. I am able to set the GPIO expander output pins (to high) using the i2cset command:
sudo i2cset 1 0x20 0x02 0xFF // 0x20 (gpio expander), and register 0x02
I came across a kernel driver for the PCA953X and loaded the kernel module gpio-pca953x.ko + modified the /boot/config.txt to include the dts overlay for the expander. When I run i2detect -y 1 (for i2c-1 bus), I can see "UU" at the 0x20 slave address, which corroborates that the driver is managing it.
If I wanted to do something comparable to what I did with the i2cset command programmatically, what API / interface should I use such that I make use of the PCA953X driver that's installed? As you can see from the code below, nothing there suggests that I would be utilizing PCA953X:
int file;
int adapter_nr = 1;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if (file < 0) {
exit(1);
}
// Writes 0xFF to register 0x02
uint8_t cmd[2] = {0x02, 0XFF};
struct i2c_msg msg = {
.addr = 0x20,
.flags = 0,
.len = sizeof(cmd)/sizeof(uint8_t),
.buf = cmd
};
struct i2c_rdwr_ioctl_data tx = {
.msgs = &msg,
.nmsgs = 1
};
ioctl(file, I2C_RDWR, &tx);
Is there a programming route that involves utilizing the PCA953X driver? What does having that module actually do?
Thanks to #TomV for pointing it out in the comments. The PCA953X driver provides a new character device in the form of "gpiochipN". In my case, it was gpiochip2. I had not noticed this extra gpiochip in /dev as I was not looking for it. Because this is a character device, I was able to use ioctl to interact with the lines:
int fd, ret;
fd = open("/dev/gpiochip2", O_RDONLY);
if (fd < 0) {
printf("Unable to open: %s", strerror(errno));
return;
}
struct gpiohandle_request req;
req.lineoffsets[0] = 0;
req.lineoffsets[1] = 1;
req.lineoffsets[2] = 2;
req.lineoffsets[3] = 3;
req.lineoffsets[4] = 4;
req.lineoffsets[5] = 5;
req.lineoffsets[6] = 6;
req.lineoffsets[7] = 7;
req.lines = 8;
req.flags = GPIOHANDLE_REQUEST_OUTPUT;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (-1 == ret) {
printf("Failed to get line handle:%s\n", strerror(ret));
close(fd);
return;
}
// Sets all 8 lines to high (equivalent to setting register 0x3 to 0b11111111 or 0xFF)
struct gpiohandle_data data;
data.values[0] = 1;
data.values[1] = 1;
data.values[2] = 1;
data.values[3] = 1;
data.values[4] = 1;
data.values[5] = 1;
data.values[6] = 1;
data.values[7] = 1;
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (-1 == ret) {
printf("Failed to set line value\n");
}
else {
close(req.fd);
}
close(fd);
The above code interacts with the gpiochip2 character device, which is made possible by having the PCA953X installed. Without it, I would have to read and write to the registers through the i2c bus as was posted in my original question.

How can I toggle two LEDs at difference frequencies using SYSTICK on keil for the ST32 processor

I want to toggle each LED one aT 4Hz and one at 3Hz, so far i can only toggle 2 at the same frequency.So far i can do them separately only but i dont know how to write to code to combine them so i can run it all at the same time.
// THIS CODE IS FOR BOTH
int main (void){
//Enable clock for GPIO A and Gpio B
RCC->AHB2ENR |= 0x3UL;
//Configure PA_0 and PA_1
GPIOA->MODER &= ~0xFUL ;
GPIOA->MODER |= 0x5UL;
GPIOA-> PUPDR &= ~0XFUL;
GPIOA-> PUPDR |= 0xAUL;
//FOR LED GREEN
SysTick ->LOAD = 1000000-1 ;
SysTick-> VAL = 0;
SysTick->CTRL |= 0x5UL;
while (1)
{
if (SysTick -> CTRL & SysTick_CTRL_COUNTFLAG_Msk)
{
GPIOA->ODR ^= 0x2UL;
}
}
}
//THEN deleting LED GREEN TO WRITE LED orange
SysTick ->LOAD = 666667-1 ;
SysTick-> VAL = 0;
SysTick->CTRL |= 0x5UL;
while (1)
{
if (SysTick -> CTRL & SysTick_CTRL_COUNTFLAG_Msk)
{
GPIOA->ODR ^= 0x1UL;
}
}
}
i just need help to combine them mainly the systick->load for each led.
Do not use systick this way. Set the systick interrupt to be triggered lets say 1000 times per second (standard STM startup files do it this way)
Then toggle LEDs in the interrupt handler
volatile uint32_t count = 0;
void SysTick_Handler(void)
{
count++;
if(!(count % (1000 / 8))) GPIOA -> ODR ^= 1; // 4 blinks per secons
if(!(count % (1000 / 6))) GPIOA -> ODR ^= 2; // 3 blinks per second
}

STM32 ADC initialization without HAL

I'm trying to write some basic code without HAL to initialize and read from an ADC, but I can't get it respond properly. I'm using an STM32F103C8T6 on a blue pill dev board. Function is the following:
Pin B1 (ADC9) is connected to a 0-3.3V signal (a 12V supply voltage through a voltage divider)
When the signal goes below a constant that i've called SUPP_V_MIN_VAL, pin B9 is set. If the signal is above that value, pin B9 is cleared.
I defined SUPP_V_MIN_VAL to be 2606.0. I am casting everything as a float because, in a separate section in my code, i need to display the current ADC value as a float).
The issue is that I think I am not initializing my ADC properly, or not casting my types properly. I'm finding that the ADC reading is always 0. My initialization is below:
RCC->APB2ENR |= 0x1UL << 3; //Initialize clock for GPIOB (bit 3), if it hasn't been initialized yet
RCC->APB2ENR |= 0x1UL << 9; //Initialize clock for ADC1 (bit 9), if it hasn't been initialized yet
GPIOB->CRL &= ~(0xF0UL); //SetBar pin PB1 to analog input
//ADC1->CR1 |= 0x0UL; //Default settings are correct - ignore
ADC1->CR2 |= 0x2UL; //Enable ADC1_CR2_CONT for continuous conversion
//ADC1->SMPR1; //ADC channels in SMPR1 are not used - ignore
ADC1->SMPR2 |= 0x38000000UL; // Set SMP9 to 239.5 cycles (set bits 27-29 to 1)
ADC1->CR2 |= 0x1UL; //Set CR2_ADON to wake up ADC from sleep mode
ADC1->CR2 |= 0x1UL << 3; //Initialize calibration register
while ((ADC1->CR2 >> 3) & 0x1UL); //Wait until calibration register is initialized
ADC1->CR2 |= 0x1UL << 2; //Enable calibration
while ((ADC1->CR2 >> 2) & 0x1UL); //Wait until calibration completed
ADC1->CR2 |= 0x1UL; //Set CR2_ADON again to turn on ADC and start converting
I am reading the pin with:
void pollSUPP(void)
{
uint16_t ADC_Reading = ADC1->DR;
suppVolt.float_var = (float) ADC_Reading;
if( suppVolt.float_var < SUPP_V_MIN_VAL)
{
// digitalWrite(SUPP_LOW, HIGH); PB9
GPIOB->BSRR = 0X1 << 9;
}
else {
GPIOB->BRR = 0X1 << 9;
}
}
SuppVolt is defined as
union {
float float_var;
uint8_t chars[4];
} suppVolt; //ADC reading for supply voltage
I may also be doing something stupid (I haven't actually confirmed that Pin B9 is on ADC1, but I am assuming it is since the blue pill only has one ADC??) or referencing the wrong bit in a register.
Can anyone help me spot my error? Thank you in advance!!

STM32F103C8T6 can not communicate with HD44780

I'm trying to control a HD44780 16X2 LCD(4 bit
communication) using a STM32F103C8T6.
I connected the LCD this way:
RS => PA0
EN => PA2
RW Ground
D7 => PB7
D6 => PB6
D5 => PB5
D4 => PB4
The LCD doesn't display anything. Where might the problem be? Does anyone know something about this issue?
Here is my code:
#include "delay.h"
#include "stm32f10x.h" // Device header
void lcd_command(unsigned char command);
void lcd_init(void);
void lcdPosition(unsigned int row, unsigned int column);
void lcd_data(unsigned char data);
int main() {
delay_init();
RCC->APB2ENR |= 1 << 2; // Port A Enabled.
RCC->APB2ENR |= 1 << 3; // Port B Enabled.
GPIOA->CRL = 0x22222222; // A0 and A2 Output.
GPIOB->CRL = 0x22222222; // B7,B6,B5,B4 Output.
GPIOB->ODR = 0x00; // Port B clear.
delay_ms(20);
lcd_command(0x30); // Datasheet says.
delay_ms(5);
lcd_command(0x30); // Datasheet says.
delay_ms(1);
lcd_command(0x30); // Datasheet says.
delay_ms(1);
lcd_init();
lcdPosition(1, 1); // first row first column
delay_ms(1);
lcd_data('A'); // Letter A
while (1)
;
}
void lcd_command(unsigned char command) {
GPIOA->BRR |= 1 << 0; // RS reset.
GPIOA->BSRR |= 1 << 2; // E set.
GPIOB->ODR = command; // upper nibble
delay_ms(2); // delay
GPIOA->BRR |= 1 << 2; // E reset.
GPIOB->BRR = 0x000000F0; // clear data bits
delay_ms(2); // delay
command = command << 4; // lower nibble
GPIOA->BSRR |= 1 << 2; // E set.
GPIOB->ODR = command; // lower nibble
delay_ms(2); // delay
GPIOA->BRR |= 1 << 2; // E reset.
GPIOB->BRR = 0x000000FF; // clear data bits
}
void lcd_init() {
lcd_command(0x02); // Return
delay_ms(2); // delay
lcd_command(0x28);
set 4 - bit data, 2 - line, 5x7 font delay_ms(2); // delay
lcd_command(0x0C);
turn on display, cursor off.delay_ms(2); // delay
lcd_command(0x01); // Clear.
delay_ms(2); // delay
lcd_command(0x06);
move cursor right delay_ms(4); // delay
}
void lcdPosition(unsigned int row, unsigned int column) {
if (row == 1) {
column--;
lcd_command(0x80 + column); // Define row
} else if (row == 2) {
column--;
lcd_command(0xC0 + column); // Define column
}
}
void lcd_data(unsigned char data) {
GPIOA->BSRR |= 1 << 0; // RS reset.
GPIOA->BSRR |= 1 << 2; // E set.
GPIOB->ODR = data;
upper nibble first delay_ms(4); // delay
GPIOA->BRR |= 1 << 2; // E reset.
GPIOB->BRR = 0x000000F0; // clear data bits
delay_ms(4); // delay
data = data << 4; // lower nibble
GPIOA->BSRR |= 1 << 2; // E set.
GPIOB->ODR = data; // lower nibble
delay_ms(4); // delay
GPIOA->BRR |= 1 << 2; // E reset.
GPIOB->BRR = 0x000000FF; // clear data bits
}
Also PA0 and PA2 are not 5V tolerant pins, usually HD44780 is 5V device, if it runs on +5V refer to the datasheet in Table 5. "Medium-density STM32F103xx pin definitions", if the pin is 5V tolerant in column I/O level it should be denoted with FT!
Please read carefully how to initialize 4-bit interface in the datasheet at page 46, figure 24.
In short: your lcd_command sends two nibbles one after another, but you are required to send only one nibble for first several commands (since the display controller still is in the 8-bit mode). You'll need to have a separate function to send only one nibble.

How to use Backup SRAM as EEPROM in STM32F4

There are two ways of emulating EEPROM on the STM32F4:
On-chip 4 Kbytes backup SRAM
On-chip Flash, with specific software algorithm
The second option is described here: AN3969.
But google, unfortunately, hasn't been able to provide information on how to use the first option - using the 4Kb of backup SRAM as EEPROM?..
Can anyone help on the topic?
must do these:
Enable the PWR clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
Enable access to the backup domain
PWR_BackupAccessCmd(ENABLE);
Enable backup SRAM Clock
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
Enable the Backup SRAM low power Regulator to retain it's content in VBAT mode
PWR_BackupRegulatorCmd(ENABLE);
and you can write/read datas to sram (these codes from BKP_Domain codes in STM32F4xx_DSP_StdPeriph_Lib) (in my mcu stm32f417 BKPSRAM_BASE=0x40024000)
// Write to Backup SRAM with 32-Bit Data
for (i = 0x0; i < 0x100; i += 4) {
*(__IO uint32_t *) (BKPSRAM_BASE + i) = i;
}
// Check the written Data
for (i = 0x0; i < 0x100; i += 4) {
if ((*(__IO uint32_t *) (BKPSRAM_BASE + i)) != i){
errorindex++;
}
}
then if you want:
// Wait until the Backup SRAM low power Regulator is ready
while(PWR_GetFlagStatus(PWR_FLAG_BRR) == RESET)
{}
you can find these functions in STM32F4xx_DSP_StdPeriph_Lib.
after reading through the Reference Manual for stm32f4 and the stm32f405xx/stm32f407xx datasheet I agree that it isn't clear how to actually use the backup sram (or where it is located). Here is what I found. Both the RTC registers and backup SRAM contain some amount of storage that is maintained as long as you have battery power. The RTC contains 20 registers (80 bytes) and the backup sram (which is its own peripheral on AHB1 and located within the register address region) contains 0x1000 (4096 bytes). Neither are enabled by default.
in DM00037051 (stm32f405xx/stm32f407xx datasheet, p29):
The 4-Kbyte backup SRAM is an EEPROM-like memory area. It can be used to store
data which need to be retained in VBAT and standby mode. This memory area is
disabled by default to minimize power consumption (see Section 2.2.19:
Low-power modes). It can be enabled by software.
The backup registers are 32-bit registers used to store 80 bytes of user
application data when VDD power is not present. Backup registers are not reset
by a system, a power reset, or when the device wakes up from the Standby mode
(see Section 2.2.19: Low-power modes).
on page 71 of datasheet and p65 of the reference manual
AHB1 | 0x4002 4000 - 0x4002 4FFF | BKPSRAM
and page 73 of the datatasheet and p67 of the reference manual
APB1 | 0x4000 2800 - 0x4000 2BFF | RTC & BKP Registers
Page 118-119 of the reference manual contains information on enabling the backup SRAM and RTC registers.
NOTE: if you are already using the RTC in the backup domain and only need to store <= 80 bytes, then you are better off using the RTC backup registers because enabling the backup SRAM will basically double the current consumption (see table 25 in the stm32f405/7 datasheet).
here are my write and read functions for both backup SRAM and backup RTC registers
int8_t write_to_backup_sram( uint8_t *data, uint16_t bytes, uint16_t offset ) {
const uint16_t backup_size = 0x1000;
uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
}
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
/* disable backup domain write protection */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // set RCC->APB1ENR.pwren
PWR_BackupAccessCmd(ENABLE); // set PWR->CR.dbp = 1;
/** enable the backup regulator (used to maintain the backup SRAM content in
* standby and Vbat modes). NOTE : this bit is not reset when the device
* wakes up from standby, system reset or power reset. You can check that
* the backup regulator is ready on PWR->CSR.brr, see rm p144 */
PWR_BackupRegulatorCmd(ENABLE); // set PWR->CSR.bre = 1;
for( i = 0; i < bytes; i++ ) {
*(base_addr + offset + i) = *(data + i);
}
PWR_BackupAccessCmd(DISABLE); // reset PWR->CR.dbp = 0;
return 0;
}
int8_t read_from_backup_sram( uint8_t *data, uint16_t bytes, uint16_t offset ) {
const uint16_t backup_size = 0x1000;
uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE;
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
}
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
for( i = 0; i < bytes; i++ ) {
*(data + i) = *(base_addr + offset + i);
}
return 0;
}
int8_t write_to_backup_rtc( uint32_t *data, uint16_t bytes, uint16_t offset ) {
const uint16_t backup_size = 80;
volatile uint32_t* base_addr = &(RTC->BKP0R);
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
} else if( offset % 4 || bytes % 4 ) {
/* ERROR: data start or num bytes are not word aligned */
return -2;
} else {
bytes >>= 2; /* divide by 4 because writing words */
}
/* disable backup domain write protection */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // set RCC->APB1ENR.pwren
PWR_BackupAccessCmd(ENABLE); // set PWR->CR.dbp = 1;
for( i = 0; i < bytes; i++ ) {
*(base_addr + offset + i) = *(data + i);
}
PWR_BackupAccessCmd(DISABLE); // reset PWR->CR.dbp = 0;
// consider also disabling the power peripherial?
return 0;
}
int8_t read_from_backup_rtc( uint32_t *data, uint16_t bytes, uint16_t offset ) {
const uint16_t backup_size = 80;
volatile uint32_t* base_addr = &(RTC->BKP0R);
uint16_t i;
if( bytes + offset >= backup_size ) {
/* ERROR : the last byte is outside the backup SRAM region */
return -1;
} else if( offset % 4 || bytes % 4 ) {
/* ERROR: data start or num bytes are not word aligned */
return -2;
} else {
bytes >>= 2; /* divide by 4 because writing words */
}
/* read should be 32 bit aligned */
for( i = 0; i < bytes; i++ ) {
*(data + i) = *(base_addr + offset + i);
}
return 0;
}
I had to jump from main program to bootloader on user request.
So I put some 'magic number' into BKPSRAM in main program, do CPU soft reset.
Bootloader always starts first.
It checks for 'magic number' if it is present, it executes, else starts main program
when using HAL this is how to jump to bootloader:
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
__BKPSRAM_CLK_ENABLE();
*(__IO uint8_t *)0x40024000 = 42;//magic number
HAL_NVIC_SystemReset();
inside bootloader to read magic number it is enough to enable backup sram clock only (bootloader uses StdPeriphDriver).
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
extRequest = *(__IO uint8_t *)0x40024000;
if(extRequest == 42)
//run bootloader
cpu is stm32f407
Here is the example of HAL library to use backup SRAM.
#define WRITE_READ_ADDR 0x01 //offset value.you can change according to your application
uint32_t write_arr = 0xA5A5A5A6;
uint32_t read_arr;
int main()
{
enable_backup_sram();
writeBkpSram(write_arr);
while(1)
{
read_arr = readBkpSram();
}
}
void enable_backup_sram(void)
{
/*DBP : Enable access to Backup domain */
HAL_PWR_EnableBkUpAccess();
/*PWREN : Enable backup domain access */
__HAL_RCC_PWR_CLK_ENABLE();
/*BRE : Enable backup regulator
BRR : Wait for backup regulator to stabilize */
HAL_PWREx_EnableBkUpReg();
/*DBP : Disable access to Backup domain */
HAL_PWR_DisableBkUpAccess();
}
void writeBkpSram(uint32_t l_data)
{
/* Enable clock to BKPSRAM */
__HAL_RCC_BKPSRAM_CLK_ENABLE();
/* Pointer write on specific location of backup SRAM */
(uint32_t *) (BKPSRAM_BASE + WRITE_READ_ADDR) = l_data;
/* Disable clock to BKPSRAM */
__HAL_RCC_BKPSRAM_CLK_DISABLE();
}
uint32_t readBkpSram(void)
{
uint32_t i_retval;
/* Enable clock to BKPSRAM */
__HAL_RCC_BKPSRAM_CLK_ENABLE();
/* Pointer write from specific location of backup SRAM */
i_retval = *(uint32_t*) (BKPSRAM_BASE + WRITE_READ_ADDR);
/* Disable clock to BKPSRAM */
__HAL_RCC_BKPSRAM_CLK_DISABLE();
return i_retval;
}
I'm currently using the an STM32F2xx microcontroller. According to the datasheet:
The 4-Kbyte backup SRAM is an EEPROM-like area.
To retain the content of the RTC backup registers … when VDD is turned off, VBAT pin can be connected to an optional standby voltage supplied by a battery or by another source.
A supercap, for example, would be required to maintain the contents of the backup registers while the microcontroller is powered off.
Also, according to the document:
After reset, the backup domain (… backup SRAM) is protected against possible unwanted write accesses. To enable access to the backup domain, proceed as follows …
It gives you instructions on how to gain access to the backup domain by directly writing to the certain peripheral register. If you have access to the STM32F4xx library, you can call something like this (note: I'm using the STM32F2xx library):
PWR_BackupAccessCmd(ENABLE);
Note: There's is more to it than simply calling the above function, such as enabling the backup SRAM interface clock. Consult the STM32F4 series documentation.
There is a lot of documentation embedded in the library source that is invaluable and if it's available should be read.
On the STM32F2 series microcontroller, SRAM is located at the following memory address range:
0x40024000 - 0x40024FFF
And can be written to somewhere at location, for example, as follows:
#define VAR_LOC ((volatile uint8_t *)(0x40024000))
volatile uint8_t *pVar = VAR_LOC;
*pVar = 5;
Useable example
In header:
//------------------------------------
typedef struct
{
uint32_t isDefault; //must by 0x12345678
uint32_t LastTestNumber;
uint32_t LastUserNumber;
uint32_t LastModeTest;
uint32_t calibv;
uint32_t calibc;
uint32_t WorkTime;
int32_t RTCCalib;
uint32_t LCDContrast;
} sBKPSRAM;
extern sBKPSRAM *BKPSRAM;// = (sSDRAM *)SDRAM_BANK_ADDR;
//------------------------------------
In code head
define as data:
sBKPSRAM *BKPSRAM = (sBKPSRAM *)BKPSRAM_BASE;
In Init:
void main(void)
{
(....)
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);
PWR_BackupAccessCmd(ENABLE);
PWR_BackupRegulatorCmd(ENABLE);
ifDefault();
(....)
}
In procedure:
//-------------------------------------------------
void ifDefault(void)
{
if (BKPSRAM->LastModeTest!=0x12345678)
{
printf("BKPSRAM to default\r\n");
memset(BKPSRAM,0,sizeof(sBKPSRAM));
BKPSRAM->calibv =66920;
BKPSRAM->calibc =79230;
BKPSRAM->RTCCalib =1;
BKPSRAM->LCDContrast =2;
BKPSRAM->LastModeTest =0x12345678;
}
}
//-------------------------------------------------
HAL Configuration for STM32H7 to access backup SRAM:
#define BKP_RAM (*(__IO uint32_t *) (D3_BKPSRAM_BASE)) //Start address: 0x38800000
Main() {
__HAL_RCC_BKPRAM_CLK_ENABLE();
HAL_PWREx_EnableBkUpReg();
BKP_RAM = 0xA5AA5A55;
}
In addition to that, you need to add a below line in systemInit() to enable write-through access to Backup SRAM.
SCB->CACR |= 1<<2;