Can attaching to the target be done automatically in IAR debugger? - debugging

I have the device which periodically is reseted. I want debugger to be attached to STM32 all time. Might just automatic start of debugging (after it stops after NVIC_SystemReset) will be anough.
I do periodically the NVIC_SystemReset()
__STATIC_INLINE void NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding
memory accesses included
buffered write are completed before reset */
SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
for(;;) /* wait until reset */
{
__NOP();
}
}

Related

dsPIC33 Flash Erase broken

I am having a lot of trouble when it comes to flash erasing on the dsPIC33EP64GP503 and I am hoping someone on here will be able help.
I am wanting to store a data struct in the flash program memory of the device. I am having trouble when it comes to erasing the flash though. I need to erase it and re-write it when the data changes.
I am padding the rest of the page with 0s so it can be safely erased.
I can write to the same memory location of the struct. When doing a flash write onto the start of the struct, the byStructValid turns into 0x11 (I know this is all very bad, because it is writing double word. But I am just trying to get the flash operations working first), however when I do an erase nothing happens. Is someone able to figure out what I am doing wrong?
I initialised the struct with 0xFF's and tried to perform a flash write. This was successful as the CAN message I received showed the data changed from 0xFF to 0x11.
I then tried to do a flash erase, but nothing happened. The device just carried on as normal. I don't have access to debug so it is hard to fully understand what is going on during this time.
I have tried moving the struct location around, so that it is on an 'even' page boundary (as specified in the datasheet) but this hasn't worked either.
I have also tried using an assembly version of the erase function, provided by the datasheet, this also doesn't work. The device just carries on as though there was no command for flash erase.
Below are some snippets of code that I have been using.
Any help would be greatly appreciated, thank you.
Note: I am unable to use the debugger. I use CAN messages to periodically send ‘debug’ messages, which contain data that is read from the flash location. This is so I can see if the write/erases are working.
#define MEMORY_USER_CALIBRATION_LOC 0x006000
typedef struct
{
byte byStructValid;
byte abyStructData[3];
}stFlashStruct_t;
volatile const __prog__ stFlashStruct_t stFlashStruct __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) =
{
.byStructValid = 0xFF,
.abyStructData = {50, 10, 20},
};
const byte padding[_FLASH_PAGE*2 - sizeof(stFlashStruct_t)] __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC + sizeof(stFlashStruct_t)))) = {0};
//FLASH Write
void FLASH_WriteDoubleWord(dword address, dword data[2])
{
word INTCON2Save;
word i;
//set WREN and ERASE settings for operation
NVMCON = 0x4001;
TBLPAG = 0xFA;
//set address to erase
NVMADR = address & 0xFFFF;
NVMADRU = (address >> 16) & 0x3F;
for (i = 0; i < 2; i++)
{
__builtin_tblwtl(i*2, data[i] & 0xFFFF);
__builtin_tblwth(i*2, (data[i] >> 16) & 0xFF);
}
//save the interrupt register
INTCON2Save = INTCON2;
// Disable interrupts for NVM unlock
__builtin_disable_interrupts();
__builtin_write_NVM();
// Start write cycle
while(NVMCONbits.WR == 1);
//restore interrupts
INTCON2 = INTCON2Save;
}
//FLASH Erase
void FLASH_ErasePageC(dword dwAddress)
{
word INTCON2Save;
//set WREN and ERASE settings for operation
NVMCON = 0x4003;
//set address to erase
NVMADRU = (dwAddress >> 16) & 0x3F;
NVMADR = dwAddress & 0xFFFF;
//save the interrupt register
INTCON2Save = INTCON2;
__builtin_disable_interrupts();
// Disable interrupts for NVM unlock
__builtin_write_NVM();
// Start write cycle
while(NVMCONbits.WR == 1);
//restore interrupts
INTCON2 = INTCON2Save;
}
byte temp_flash_write(void)
{
dword new_data[2] = {0x1111, 0x1111};
FLASH_WriteDoubleWord(&stCustomerCalibration, new_data);
return 0;
}
Your "dsPIC33 Flash Erase broken" issue is one of not understanding just how badly the Run Time Flash Programming (RTFP) method is described in the Microchip dsPIC33EP64GP503 data sheet and family reference manuals.
This post will not explain how any of this works. It does work but is really hard to comprehend.
What will be hard for you is that a program flash word can only be written one time after an erase. Writing to the same program flash word a second time will corrupt it and the next time it is read an ECC trap error will assert.
Attached is example code that allocates a 1024 instruction word page at address 0x6000. Declares a structure at the start of that page that is 2 instruction words in size. The code then erases that page then writes different data to the first 2 instruction words in that page.
/*
* File: main.c
* Author: Dan1138
*
* Description:
* Example for Run Time Self Programming (RTSP).
* This is very limited, useful as a test bench but not much more.
*
* Created on December 10, 2022, 2:05 PM
*/
/* Define the system oscillator frequency this code must configure */
#define FSYS (7372800ul)
#define FCY (FSYS/2ul)
// DSPIC33EP64GP503 Configuration Bit Settings
// 'C' source line config statements
// FICD
#pragma config ICS = PGD1 // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
// FPOR
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
// FWDT
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
// FOSC
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = ON // OSC2 Pin Function bit (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
// FOSCSEL
#pragma config FNOSC = FRC // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config IESO = ON // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)
// FGS
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#include <libpic30.h>
#define MEMORY_USER_CALIBRATION_LOC (_FLASH_PAGE * 24)
typedef struct
{
uint8_t byStructValid;
uint8_t abyStructData[3];
} stFlashStruct_t;
volatile const __prog__ __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) union
{
uint16_t words[_FLASH_PAGE]; /* reserve the entire erase page. Note only the low 16-bits of the instruction word can be accessed with this method. */
struct {
stFlashStruct_t stFlashStruct; /* calibration structure */
};
} CalSpace =
{
.stFlashStruct.byStructValid = 0xFF,
.stFlashStruct.abyStructData = {50, 10, 20},
};
int main(void)
{
volatile stFlashStruct_t ReadBack;
/*
* application initialization
*/
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/* Erase 1024 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
NVMCON = 0x4003;
NVMADR = __builtin_tbloffset(&CalSpace);
NVMADRU = __builtin_tblpage(&CalSpace);
__builtin_disi(5); // Disable interrupts for NVM unlock
__builtin_write_NVM(); // Start write cycle
while(NVMCONbits.WR == 1);
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/* Update data in structure to be written */
ReadBack.byStructValid = 1;
ReadBack.abyStructData[0] = 2;
ReadBack.abyStructData[1] = 3;
ReadBack.abyStructData[2] = 4;
/* Write 2 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
NVMCON = 0x4001; // Set WREN and word program mode
TBLPAG = 0xFA; // write latch upper address
NVMADR = __builtin_tbloffset(&CalSpace.stFlashStruct);
NVMADRU = __builtin_tblpage(&CalSpace);
__builtin_tblwtl(0,*((uint16_t *)(&ReadBack)+0)); // load low 16-bits of first instruction word
__builtin_tblwth(0,0x00); // make high 8-bits of first instruction word zero
__builtin_tblwtl(2,*((uint16_t *)(&ReadBack)+1)); // load low 16-bits of second instruction word
__builtin_tblwth(2,0x00); // make high 8-bits of second instruction word zero
__builtin_disi(5); // Disable interrupts for NVM unlock sequence
__builtin_write_NVM(); // initiate write
while(NVMCONbits.WR == 1);
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/*
* Application process loop
*/
for(;;)
{
Nop();
Nop();
Nop();
__delay_ms(100);
}
}

Cannot access memory locations while sending buffer via UART as USB data to VCP

I am implementing a simple found here
https://shawnhymel.com/1795/getting-started-with-stm32-nucleo-usb-virtual-com-port/
In a super while loop, when debugging
while (1)
{
/* USER CODE END WHILE */
uint8_t buffer[] = "hello world";
**CDC_Transmit_FS(buffer,sizeof(buffer));**
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
Board:nucleo-L432KC
STM32CodeIDE
Attach is the image of error while debugging.

Trying to send 10-bit data over SPI as Slave when receiving Clock and Slave Select as input(with buffer mode)

I'm trying to emulate data from the 10-bit AEAT-6010 rotary encoder and send it out on the MISO pin of the SPI protocol on the ATtiny3217. The ATtiny act as a slave and receive a CLK and SS signal as input to respond to by outputting the data(encoder value). The encoder follows the SSI protocol to send data as shown below:
The problem arrises when trying to send 10 bits voer the 8 bit SPI protocol on the ATtiny. The master of the SPI protocol is the TMS320F2808 chip and from it I receive 11 clock pulses and the SS signal. The measured signals and data is shown below:
Here the data I try to send is 0b10 just for testing. I can see the correct data on the MISO line, but there is three 1's extra in the middle of the signal. This is with BUFEN=1 and BUFWR=1 in the SPI settings of the ATtiny as can be seen in the configuration below(without buffer mode the three 1's comes on the last 3 bits, and aslo the first bit(MSB) is read as a 1):
int8_t SPI_0_init()
{
SPI0.CTRLA = 0 << SPI_CLK2X_bp /* Enable Double Speed: disabled */
| 0 << SPI_DORD_bp /* Data Order Setting: enabled */
| 1 << SPI_ENABLE_bp /* Enable Module: enabled */
| 0 << SPI_MASTER_bp /* SPI module in slave mode */
| SPI_PRESC_DIV4_gc; /* System Clock / 4 */
SPI0.CTRLB = 1 << SPI_BUFEN_bp /* Buffer Mode Enable: enabled */
| 1 << SPI_BUFWR_bp /* Buffer Write Mode: enabled */
| SPI_MODE_0_gc /* SPI Mode 1 */
| 0 << SPI_SSD_bp; /* Slave Select Disable: disabled */
SPI0.INTCTRL = 0 << SPI_DREIE_bp /* Data Register Empty Interrupt Enable: enabled */
| 1 << SPI_IE_bp /* Interrupt Enable: enabled */
| 0 << SPI_RXCIE_bp /* Receive Complete Interrupt Enable: disabled */
| 0 << SPI_SSIE_bp /* Slave Select Trigger Interrupt Enable: disabled */
| 0 << SPI_TXCIE_bp; /* Transfer Complete Interrupt Enable: disabled */
return 0;
}
I have confirmed that the data fromat of the SPI module of the TMS320F2808 and the ATtiny is the same(read on falling clock edge). Is there something I am missing about how the buffers of the SPI work or the interrupts of the SPI? I'm not sure what to to in the ISR when enabling different interrupts(oher than clearing the flags). This is my main function(The ISR is empty for now):
int main(void)
{
/* Initializes MCU, drivers and middleware */
atmel_start_init();
sei(); // Enable global interrupts
/* Replace with your application code */
while (1) {
SPI_transmit(enc_data_L);
}
Where the SPI_transmit() is as follows:
void SPI_transmit(uint16_t enc_data)
{
// Then start the transmission by assigning the data to the SPI data register
SPI0.DATA = enc_data;
//SPI0.DATA = enc_data_H;
// Now wait for the data transmission to complete by periodically checking the SPI status register
//the SPI_IF is the only interrupt flag with a function in non-buffered mode.
while(!(SPI0.INTFLAGS & (SPI_RXCIF_bm)));
SPI0.DATA; //Dummy read to clear flag
}
I have also tried splitting the 16 bit data, as suggested here, but the problem remains also for 8-bit data. Hope this is enough background info. I'm thankful for any ideas!
in SPI_transmit after writing to the buffer you're waiting until transmission is completed. After that the input buffer is being fully transmitted and now is empty.
while(!(SPI0.INTFLAGS & (SPI_RXCIF_bm))); // waits until data is transmitted
RXCIF is set when there is a data in the receive buffer, i.e. the transmission of the output buffer was also completed.
Therefore, when next byte is being transmitted by the master, there is no new data yet in the output buffer to transmit. Probably the SPI module just repeats transmission of the previous byte.
So, instead of waiting for RXCIF flag, wait for DREIF flag, which will be set, when the transmission buffer is empty and ready for the next byte.
You can just ignore the incoming data, since you're not use it anyhow
void SPI_transmit(uint16_t enc_data)
{
// Wait until buffer is ready to get the next data byte
while(!(SPI0.INTFLAGS & (SPI_DREIF_bm)));
// Then start the transmission by assigning the data to the SPI data register
SPI0.DATA = enc_data;
// Don't care about read buffer
}

Why does a printer's orientation setting get incorrectly reported by DocumentProperties() if program is executed from a windows service

I have a VC++ console app which has a function that reads the printer's current orientation setting. Using the sample code from this MS page, I can successfully read the printer's setting when executing the program from the cmd prompt. However, if I execute it from a Windows service (written in C#), the current orientation would always return as 1 (portrait), even though the other settings look correct. Why is that?
To summarize:
For a printer whose orientation is set to Landscape, the code below, if run from cmd.exe, correctly outputs:
original printer orientation=2
but if run from a windows service written with C#, always outputs:
original printer orientation=1
/*
* Step 1:
* Allocate a buffer of the correct size.
*/
dwNeeded = DocumentProperties(NULL,
hPrinter, /* Handle to our printer. */
deviceName, /* Name of the printer. */
NULL, /* Asking for size, so */
NULL, /* these are not used. */
0); /* Zero returns buffer size. */
pDevMode = (LPDEVMODE)malloc(dwNeeded);
/*
* Step 2:
* Get the default DevMode for the printer and
* modify it for your needs.
*/
dwRet = DocumentProperties(NULL,
hPrinter,
deviceName,
pDevMode, /* The address of the buffer to fill. */
NULL, /* Not using the input buffer. */
DM_OUT_BUFFER); /* Have the output buffer filled. */
if (dwRet != IDOK)
{
/* If failure, cleanup and return failure. */
free(pDevMode);
ClosePrinter(hPrinter);
return NULL;
}
cout << "original printer orientation=";
cout << pDevMode->dmOrientation;
This is due to permissions. The Windows service was running under "Local System", which of course does not recognize the printer setting changes I made under my own logon name. Thanks to Retired Ninja who made me think of permissions.

Why is this DMA I2C transfer locking up execution?

I'm trying to change a library for STM32F407 to include DMA transfers when using I2C. I'm using it do drive an OLED screen. In its original form it is working w/o problems. In the comments, somebody added DMA, but also ported it to STM32F10 and I'm trying to port it back to F407.
My problem is, after enabling DMA transfer, debugger stops working (at exactly that line) - debugger activity LED stops / turns off and debugger stays at next statement.
After some more testing (blinking a led at certain events to see if they happen) I found out that code actually continues to a certain point (specifically, next time when DMA transfer is needed - in second call to update screen). After that, program doesn't continue (LED doesn't turn ON if set ON after that statement).
The weird thing is, I know the transfer is working because the screen gets a few characters written on it. That only happens if I don't debug step by step because CPU writes new data to screen buffer in the mean time and changes content of it before it is entirely sent to the screen by DMA (I will figure out how to fix that later - probably dual buffer, but it shouldn't interfere with DMA transfer anyway). However if I debug step by step, DMA finishes before CPU writes new content to screen buffer and screen is black (as it should be as buffer is first cleared). For testing, I removed the first call to DMA (after the clearing of buffer) and let the program write the text intended into buffer. It displays without any anomalies, so that means DMA must have finished, but something happened after. I simply can't explain why debugger stops working if DMA finishes the transfer.
I tried blinking a led in transfer finished interrupt handler of DMA but it never blinks, that means it is never fired. I would appreciate any help as I'm at a loss (been debugging for a few days now).
Thank you!
Here is relevant part of code (I have omitted rest of the code because there is a lot of it, but if required I can post). The code works without DMA (with ordinary I2C transfers), it only breaks with DMA.
// TM_STM32F4_I2C.h
typedef struct DMA_Data
{
DMA_Stream_TypeDef* DMAy_Streamx;
uint32_t feif;
uint32_t dmeif;
uint32_t teif;
uint32_t htif;
uint32_t tcif;
} DMA_Data;
//...
// TM_STM32F4_I2C.c
void TM_I2C_Init(I2C_TypeDef* I2Cx, uint32_t clockSpeed) {
I2C_InitTypeDef I2C_InitStruct;
/* Enable clock */
RCC->APB1ENR |= RCC_APB1ENR_I2C3EN;
/* Enable pins */
TM_GPIO_InitAlternate(GPIOA, GPIO_PIN_8, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);
TM_GPIO_InitAlternate(GPIOC, GPIO_PIN_9, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);
/* Check clock, set the lowest clock your devices support on the same I2C bus */
if (clockSpeed < TM_I2C_INT_Clocks[2]) {
TM_I2C_INT_Clocks[2] = clockSpeed;
}
/* Set values */
I2C_InitStruct.I2C_ClockSpeed = TM_I2C_INT_Clocks[2];
I2C_InitStruct.I2C_AcknowledgedAddress = TM_I2C3_ACKNOWLEDGED_ADDRESS;
I2C_InitStruct.I2C_Mode = TM_I2C3_MODE;
I2C_InitStruct.I2C_OwnAddress1 = TM_I2C3_OWN_ADDRESS;
I2C_InitStruct.I2C_Ack = TM_I2C3_ACK;
I2C_InitStruct.I2C_DutyCycle = TM_I2C3_DUTY_CYCLE;
/* Disable I2C first */
I2Cx->CR1 &= ~I2C_CR1_PE;
/* Initialize I2C */
I2C_Init(I2Cx, &I2C_InitStruct);
/* Enable I2C */
I2Cx->CR1 |= I2C_CR1_PE;
}
int16_t TM_I2C_WriteMultiDMA(DMA_Data* dmaData, I2C_TypeDef* I2Cx, uint8_t address, uint8_t reg, uint16_t len)
{
int16_t ok = 0;
// If DMA is already enabled, wait for it to complete first.
// Interrupt will disable this after transmission is complete.
TM_I2C_Timeout = 10000000;
// TODO: Is this I2C check ok?
while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) && DMA_GetCmdStatus(dmaData->DMAy_Streamx) && TM_I2C_Timeout)
{
if (--TM_I2C_Timeout == 0)
{
return -1;
}
}
//Set amount of bytes to transfer
DMA_Cmd(dmaData->DMAy_Streamx, DISABLE); //should already be disabled at this point
DMA_SetCurrDataCounter(dmaData->DMAy_Streamx, len);
DMA_ClearFlag(dmaData->DMAy_Streamx, dmaData->feif | dmaData->dmeif | dmaData->teif | dmaData->htif | dmaData->tcif); // Clear dma flags
DMA_Cmd(dmaData->DMAy_Streamx, ENABLE); // enable DMA
//Send I2C start
ok = TM_I2C_Start(I2Cx, address, I2C_TRANSMITTER_MODE, I2C_ACK_DISABLE);
//Send register to write to
TM_I2C_WriteData(I2Cx, reg);
//Start DMA transmission, interrupt will handle transmit complete.
I2C_DMACmd(I2Cx, ENABLE);
return ok;
}
//...
// TM_STM32F4_SSD1306.h
#define SSD1306_I2C I2C3
#define SSD1306_I2Cx 3
#define SSD1306_DMA_STREAM DMA1_Stream4
#define SSD1306_DMA_FEIF DMA_FLAG_FEIF4
#define SSD1306_DMA_DMEIF DMA_FLAG_DMEIF4
#define SSD1306_DMA_TEIF DMA_FLAG_TEIF4
#define SSD1306_DMA_HTIF DMA_FLAG_HTIF4
#define SSD1306_DMA_TCIF DMA_FLAG_TCIF4
static DMA_Data ssd1306_dma_data = { SSD1306_DMA_STREAM, SSD1306_DMA_FEIF, SSD1306_DMA_DMEIF, SSD1306_DMA_TEIF, SSD1306_DMA_HTIF, SSD1306_DMA_TCIF };
#define SSD1306_I2C_ADDR 0x78
//...
// TM_STM32F4_SSD1306.c
void TM_SSD1306_initDMA(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
DMA_DeInit(DMA1_Stream4);
DMA_Cmd(DMA1_Stream4, DISABLE);
//Configure DMA controller channel 3, I2C TX channel.
DMA_StructInit(&DMA_InitStructure); // Load defaults
DMA_InitStructure.DMA_Channel = DMA_Channel_3;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C3->DR)); // I2C3 data register address
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SSD1306_Buffer; // Display buffer address
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // DMA from mem to periph
DMA_InitStructure.DMA_BufferSize = 1024; // Is set later in transmit function
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Do not increment peripheral address
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Do increment memory address
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA one shot, no circular.
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // Tweak if interfering with other dma actions
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream4, &DMA_InitStructure);
DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); // Enable transmit complete interrupt
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
// Set interrupt controller for DMA
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn; // I2C3 TX connect to stream 4 of DMA1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x05;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Set interrupt controller for I2C
NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
I2C_ITConfig(I2C3, I2C_IT_BTF, ENABLE);
}
extern void DMA1_Channel3_IRQHandler(void)
{
//I2C3 DMA transmit completed
if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TC) != RESET)
{
// Stop DMA, clear interrupt
DMA_Cmd(DMA1_Stream4, DISABLE);
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
I2C_DMACmd(SSD1306_I2C, DISABLE);
}
}
// Sending stop condition to I2C in separate handler necessary
// because DMA can finish before I2C finishes
// transmitting and last byte is not sent
extern void I2C3_EV_IRQHandler(void)
{
if (I2C_GetITStatus(I2C3, I2C_IT_BTF) != RESET)
{
TM_I2C_Stop(SSD1306_I2C); // send i2c stop
I2C_ClearITPendingBit(I2C3, I2C_IT_BTF);
}
}
// ...
void TM_SSD1306_UpdateScreen(void) {
TM_I2C_WriteMultiDMA(&ssd1306_dma_data, SSD1306_I2C, SSD1306_I2C_ADDR, 0x40, 1024); // Use DMA
}
edit: i noticed the wrong condition checking at initializing a new transfer, but fixing it doesn't fix the main problem
while ((I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) || !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) || DMA_GetCmdStatus(dmaData->DMAy_Streamx)) && TM_I2C_Timeout)

Resources