I don't understand what is wrong with the following code. All is correct for transmit but for the receive the interrupt isn't called.
The oscilloscope shows me correct send and receive data.
#define CONSOLE_RXD_PIN 12
#define CONSOLE_TXD_PIN 11
static USART_InitTypeDef ConsoleUartConfig = {CONSOLE_BAUD_RATE, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No, USART_Mode_Rx | USART_Mode_Tx, USART_HardwareFlowControl_None};
static GPIO_InitTypeDef HAL_TXDGpioConfiguration = {(1 << CONSOLE_TXD_PIN), GPIO_Mode_AF, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_NOPULL};
static GPIO_InitTypeDef HAL_RXDGpioConfiguration = {(1 << CONSOLE_RXD_PIN), GPIO_Mode_AF, GPIO_Speed_50MHz, GPIO_OType_PP, GPIO_PuPd_UP};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_Init(GPIOA, (GPIO_InitTypeDef *)&HAL_TXDGpioConfiguration);
GPIO_PinAFConfig(GPIOA, CONSOLE_TXD_PIN, CONSOLE_UART_GPIO_AF);
GPIO_Init(GPIOA, (GPIO_InitTypeDef *)&HAL_RXDGpioConfiguration);
GPIO_PinAFConfig(GPIOA, CONSOLE_RXD_PIN, CONSOLE_UART_GPIO_AF);
USART_Init(USART6, &ConsoleUartConfig);
USART_ITConfig(USART6, USART_IT_TXE, ENABLE);
USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = USART6_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
USART_Cmd(HAL_UartContext.Base, ENABLE);
It is a stm32f413Z nucleo board.
UPDATE:
I tried the same with the HAL but here the function USART6_IRQHandler is also not called for the rx.
HAL_Init();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART6_CLK_ENABLE();
#define CONSOLE_TXD_PIN LL_GPIO_PIN_11
/* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinSpeed(GPIOA, CONSOLE_TXD_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(GPIOA, CONSOLE_TXD_PIN, LL_GPIO_PULL_NO);
LL_GPIO_SetPinPull(GPIOA, CONSOLE_TXD_PIN, LL_GPIO_PULL_UP);
LL_GPIO_SetPinMode(GPIOA, CONSOLE_TXD_PIN, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, CONSOLE_TXD_PIN, LL_GPIO_AF_8);
#define CONSOLE_RXD_PIN LL_GPIO_PIN_12
/* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinSpeed(GPIOA, CONSOLE_RXD_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(GPIOA, CONSOLE_RXD_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(GPIOA, CONSOLE_RXD_PIN, LL_GPIO_PULL_NO);
LL_GPIO_SetPinMode(GPIOA, CONSOLE_RXD_PIN, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetAFPin_8_15(GPIOA, CONSOLE_RXD_PIN, LL_GPIO_AF_8);
NVIC_SetPriority(USART6_IRQn, 0);
NVIC_EnableIRQ(USART6_IRQn);
LL_USART_EnableIT_TXE(USART6);
LL_USART_EnableIT_RXNE(USART6);
LL_USART_SetTransferDirection(USART6, LL_USART_DIRECTION_TX_RX);
/* 8 data bit, 1 start bit, 1 stop bit, no parity */
LL_USART_ConfigCharacter(USART6, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
LL_USART_SetHWFlowCtrl(USART6, LL_USART_HWCONTROL_NONE);
/* Reset value is LL_USART_OVERSAMPLING_16 */
LL_USART_SetOverSampling(USART6, LL_USART_OVERSAMPLING_16);
const uint32_t pclk = __LL_RCC_CALC_PCLK2_FREQ(SystemCoreClock,
LL_RCC_GetAPB2Prescaler());
LL_USART_SetBaudRate(USART6, pclk, LL_USART_OVERSAMPLING_16, 9600);
LL_USART_Enable(USART6);
Here is the interrupt routine:
void USART6_IRQHandler(void)
{
uint32_t stateflg;
if (LL_USART_IsActiveFlag_RXNE(USART6) || LL_USART_IsActiveFlag_ORE(USART6))
{
LL_USART_ReceiveData8(USART6);
}
if (LL_USART_IsActiveFlag_TXE(USART6))
{
uint8_t data = 0;
}
}
The normal init structure for the GPIO pins looks like:
typedef struct
{
uint32_t GPIO_Pin;
GPIOMode_TypeDef GPIO_Mode;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOOType_TypeDef GPIO_OType;
GPIOPuPd_TypeDef GPIO_PuPd;
} GPIO_InitTypeDef;
So this did not match with your initialization. The first parameter should be the pin you are using.
Generally it would be better not to use this kind of initialization, since you could easily change parameter position. If your compiler is not to old, you could use the "named" way like:
static GPIO_InitTypeDef HAL_TXDGpioConfiguration = {
.GPIO_Pin = GPIO_Pin_9,
Additional:
USART_ITConfig(GPIOA, USART_IT_TXE, ENABLE);
USART_ITConfig(GPIOA, USART_IT_RXNE, ENABLE);
should be
USART_ITConfig(USART6, USART_IT_TXE, ENABLE);
USART_ITConfig(USART6, USART_IT_RXNE, ENABLE);
Related
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);
}
}
I am successfully programming PIC32MX250F128B using Pickit3. I have written a code where, when I press a I am getting 100 data from vibration sensor. Now if I want to get another 100 data, either I have to disconnect and then reconnect the 10k ohm pull up resistor connected to MCLR pin or have to run the program again.
Is there any other way I can reset the pickit?
Here is the code I am using:
#include <p32xxxx.h> // include chip specific header file
#include <plib.h> // include peripheral library functions
// Configuration Bits
#pragma config FNOSC = FRCPLL // Internal Fast RC oscillator (8 MHz) w/ PLL
#pragma config FPLLIDIV = DIV_2 // Divide FRC before PLL (now 4 MHz)
#pragma config FPLLMUL = MUL_20 // PLL Multiply (now 80 MHz)
#pragma config FPLLODIV = DIV_2 // Divide After PLL (now 40 MHz)
// see figure 8.1 in datasheet for more info
#pragma config FWDTEN = OFF // Watchdog Timer Disabled
#pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select
#pragma config JTAGEN = OFF // Disable JTAG
#pragma config FSOSCEN = OFF // Disable Secondary Oscillator
#pragma config FPBDIV = DIV_1 // PBCLK = SYCLK
// Defines
#define SYSCLK 40000000L
// Macros
// Equation to set baud rate from UART reference manual equation 21-1
#define Baud2BRG(desired_baud) ( (SYSCLK / (16*desired_baud))-1)
// Function Prototypes
int SerialTransmit(const char *buffer);
unsigned int SerialReceive(char *buffer); //, unsigned int max_size);
int UART2Configure( int baud);
short a2dvals[11000];
int adcptr,num_channels,k,i;
char sampling;
int ADC_RSLT0,totaldata,totaldata1,chunks_sent,data_count,l;
short temp;
BOOL a2don;
volatile unsigned int channel4;
void __ISR(_ADC_VECTOR, IPL2) TIMER3Handler(void) // Fonction d'interruption Timer 3
{
temp = ReadADC10(0);
a2dvals[k] = (temp);
k++;
if (k>totaldata1)// && sampling == 's')
{
T3CONCLR = 0x8000;
a2don=FALSE;
chunks_sent = 0;
totaldata = k/2;
k = 1;
}
mAD1ClearIntFlag();
}
int main(void)
{
char buf[1024]; // declare receive buffer with max size 1024
// Peripheral Pin Select
U2RXRbits.U2RXR = 4; //SET RX to RB8
RPB9Rbits.RPB9R = 2; //SET RB9 to TX
SYSTEMConfigPerformance(SYSCLK);
UART2Configure(9600); // Configure UART2 for a baud rate of 9600
U2MODESET = 0x8000; // enable UART2
ANSELBbits.ANSB2 = 1; // set RB2 (AN4) to analog
TRISBbits.TRISB2 = 1; // set RB2 as an input
//adcConfigureManual(); // Configure ADC
//AD1CON1SET = 0x8000; // Enable ADC
SerialTransmit("Hello! Enter 'a' to do ADC conversion \r\n");
unsigned int rx_size;
while( 1){
rx_size = SerialReceive(buf); //, 1024); // wait here until data is received
SerialTransmit(buf); // Send out data exactly as received
SerialTransmit("\r\n");
}
return 1;
} // END main()
/* UART2Configure() sets up the UART2 for the most standard and minimal operation
* Enable TX and RX lines, 8 data bits, no parity, 1 stop bit, idle when HIGH
* Input: Desired Baud Rate
* Output: Actual Baud Rate from baud control register U2BRG after assignment*/
int UART2Configure( int desired_baud){
U2MODE = 0; // disable autobaud, TX and RX enabled only, 8N1, idle=HIGH
U2STA = 0x1400; // enable TX and RX
U2BRG = Baud2BRG(desired_baud); // U2BRG = (FPb / (16*baud)) - 1
// Calculate actual assigned baud rate
int actual_baud = SYSCLK / (16 * (U2BRG+1));
return actual_baud;
} // END UART2Configure()
/* SerialTransmit() transmits a string to the UART2 TX pin MSB first
*
* Inputs: *buffer = string to transmit */
int SerialTransmit(const char *buffer)
{
unsigned int size = strlen(buffer);
while( size)
{
while( U2STAbits.UTXBF); // wait while TX buffer full
U2TXREG = *buffer; // send single character to transmit buffer
buffer++; // transmit next character on following loop
size--; // loop until all characters sent (when size = 0)
}
while( !U2STAbits.TRMT); // wait for last transmission to finish
return 0;
}
/* SerialReceive() is a blocking function that waits for data on
* the UART2 RX buffer and then stores all incoming data into *buffer
*
* Note that when a carriage return '\r' is received, a nul character
* is appended signifying the strings end
*
* Inputs: *buffer = Character array/pointer to store received data into
* max_size = number of bytes allocated to this pointer
* Outputs: Number of characters received */
unsigned int SerialReceive(char *buffer) //, unsigned int max_size)
{
//unsigned int num_char = 0;
/* Wait for and store incoming data until either a carriage return is received
* or the number of received characters (num_chars) exceeds max_size */
while(1)
{
while( !U2STAbits.URXDA); // wait until data available in RX buffer
*buffer = U2RXREG; // empty contents of RX buffer into *buffer pointer
if (*buffer == 'a')
{
int dummy,dummy1;
unsigned char tempstr[5];
SYSTEMConfig(SYSCLK, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
// the ADC ///////////////////////////////////////
// configure and enable the ADC
CloseADC10(); // ensure the ADC is off before setting the configuration
// define setup parameters for OpenADC10
// Turn module on | ouput in integer | trigger mode auto | enable autosample
// ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert)
// ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
// ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
#define PARAM1 ADC_MODULE_ON|ADC_FORMAT_INTG32 | ADC_CLK_TMR | ADC_AUTO_SAMPLING_ON //
// define setup parameters for OpenADC10
// ADC ref external | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off
#define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
//
// Define setup parameters for OpenADC10
// use peripherial bus clock | set sample time | set ADC clock divider
// ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed)
// ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm
#define PARAM3 ADC_CONV_CLK_SYSTEM | ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_Tcy2 //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy2
// define setup parameters for OpenADC10
// set AN4 and as analog inputs
#define PARAM4 ENABLE_AN4_ANA
// define setup parameters for OpenADC10
// do not assign channels to scan
#define PARAM5 SKIP_SCAN_ALL
// use ground as neg ref for A | use AN4 for input A
// configure to sample AN4
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN4 ); // configure to sample AN4
OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4, PARAM5 ); // configure ADC using the parameters defined above
ConfigIntADC10(ADC_INT_PRI_2 | ADC_INT_ON);
EnableADC10(); // Enable the ADC
INTEnableSystemMultiVectoredInt();
OpenTimer3(T3_OFF | T3_SOURCE_INT | T3_PS_1_1 ,0x3e8);
num_channels = 1;
totaldata1 = 10500;
a2don=TRUE;
T3CONSET = 0x8000;
k=0;
while(1)
{
while(a2don);
for(i=0;i<100;i++)
{
dummy = a2dvals[i]/1000 ;
tempstr[0] = dummy + 0x30;
dummy1 = a2dvals[i]- dummy*1000;
dummy = dummy1/100;
tempstr[1] = dummy + 0x30;
dummy1 = dummy1 - dummy*100;
dummy = dummy1/10;
tempstr[2] = dummy + 0x30;
dummy1 = dummy1 - dummy*10;
tempstr[3] = dummy1 + 0x30;
//tempstr[4] = "\0";
printf("%c%c%c%c \n", tempstr[0],tempstr[1],tempstr[2],tempstr[3]);
}
a2don=TRUE;
}
}
}
return 1;
}// END SerialReceive()
enter image description here
Thanks for your advices.
You do not need to reset the Pickit. If anything, that might be the least efficient way to do it (arguably).
Rather try something like this. Please note this is high level. You will need to make it work yourself.
void(main){
// Setup your things here
while(1){ // Your infinite loop
// Check if you received 'a' here
if (received_a == 1){ // You received a 'a'
send_data(); // Send your data
}
}
}
Without providing actual code you have written we will not be able to help you.
You use while(1) loops everywhere, and if you don't use a break; or return command you stay in that loop forever.
I think you don't need while(1) loops in the functions except in main(). Remove these and it should work.
Try drawing out your program flow in a flow chart, it should clear things up. Also consider using a state machine using switch/case. It makes it a lot clearer where you are in the code and it's easier to debug. Also, it's probably even better to use interrupts for adc and the serial port. You free up the pic to do other stuff while peripherals are doing stuff that takes time.
I'm relatively new to programming PIC18 micro-controllers and I keep getting a compile error 195 when I try to get my code to compile. The PIC I'm using is a PIC18F46k80, and the application I'm trying to program it for is for a piece of hardware with the PIC already embedded, so I can't change the Pin configuration (I didn't design the hardware). I'm trying to get my PIC to perform a slightly modified SPI protocol via bit-banging as the correct pins for the internal peripheral haven't been connect. I keep running into this compile error whenever I try to set RA6 or RA5, even though (I think) I've configured the pins to make them writable (not providing the internal clock). Specifically I get the error when trying to set LATA6 = x or LATA5 = x. In MPLAB X every issue of me writing LATA6 or LATA5 is highlighted. Can someone help me with writing/setting these pins?
Any instance of the follow code will cause an issue:
LATA5 = 0;
//or
LAT6 = 0;
//or
LAT6 = 1;
I can send through the entire file including headers and config files if needed :) Cheers for any help.
Here is the entire code:
#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#include<stdlib.h>
#include<xc.h>
//Define words for transfer//
uint8_t FR1IByte = 0x01;
int FR1DBytes = 0x900000;
uint8_t CSRIByte = 0x00;
uint8_t CSRCH0 = 0x10;
uint8_t CSRCH1 = 0x20;
uint8_t CSRCH2 = 0x40;
uint8_t CFTWIByte = 0x04;
uint32_t CFTWCH0 = 0x42680000;
uint32_t CFTWCH1 = 0x1F400000;
uint32_t CFTWCH2 = 0x3E800000;
//Functions to perform SPI//
void SPItransfer8( uint8_t byte)
{
// local variable declaration
int i;
for (i = 0; i <8; i++){ //compares MSB with mask. If it matches, it will transfer a 1//
if(byte & 0x80)
{
LATC5 = 1;
}
else
{
LATC5 = 0;
}
// Pulses clock for transfer of data//
LATA6 = 1;
LATA6 = 0;
// Logical shift left so that next byte can be read//
byte <<= 1;
}
}
void SPItransfer24( int bytes)
{
// local variable declaration
int i;
for (i = 0; i <24; i++);
{ //compares MSB with mask. If it matches, it will transfer a 1//
if(bytes & 0x800000)
{
LATC5 = 1;
}
else
{
LATC5 = 0;
}
// Pulses clock for transfer of data//
LATA6 = 1;
LATA6 = 0;
// Logical shift left so that next byte can be read//
bytes <<= 1;
}
}
void SPItransfer32( uint32_t bytes)
{
// local variable declaration
int i;
for (i = 0; i <32; i++);
{ //compares MSB with mask. If it matches, it will transfer a 1//
if(bytes & 0x80000000)
{
LATC5 = 1;
}
else
{
LATC5 = 0;
}
// Pulses clock for transfer of data//
LATA6 = 1;
LATA6 = 0;
// Logical shift left so that next byte can be read//
bytes <<= 1;
}
}
void main(void)
{
TRISE = 0x03;
TRISA = 0b1001111;
// Initialize the device//
SYSTEM_Initialize();
// Perform master reset on DDS to set the device to its default state (Active high on E2)//
LATE1 = 1;
LATE1 = 0;
// Delay to allow the system to load (PLL takes time to lock) //
__delay_ms(10)
//Set RA5 to low to prevent power down//
LATA5 = 0;
//Set RA6 to low for SPI clock//
LATA6 = 0;
//Transfers the Function Register 1 Information Byte//
SPItransfer8(FR1IByte);
//Transfers the Function Register 1 Data Bytes//
SPItransfer24(FR1DBytes);
//Transfers the Channel Select Register Information Byte//
SPItransfer8(CSRIByte);
//Transfers the Channel Select Register Data Byte for CH0
SPItransfer8(CSRCH0);
//Transfers the Channel Frequency Tuning Word Information Byte//
SPItransfer8(CFTWIByte);
//Transfers the Channel 0 Frequency Tuning Word//
SPItransfer32(CFTWCH0);
//Transfers the Channel Select Register Information Byte//
SPItransfer8(CSRIByte);
//Transfers the Channel Select Register Data Byte for CH1
SPItransfer8(CSRCH1);
//Transfers the Channel Frequency Tuning Word Information Byte//
SPItransfer8(CFTWIByte);
//Transfers the Channel 1 Frequency Tuning Word//
SPItransfer32(CFTWCH1);
//Transfers the Channel Select Register Information Byte//
SPItransfer8(CSRIByte);
//Transfers the Channel Select Register Data Byte for CH2
SPItransfer8(CSRCH2);
//Transfers the Channel Frequency Tuning Word Information Byte//
SPItransfer8(CFTWIByte);
//Transfers the Channel 2 Frequency Tuning Word//
SPItransfer32(CFTWCH2);
//Toggle I/O_Update to load data into DDS//
PORTEbits.RE2 = 1;
PORTEbits.RE2 = 0;
//Loop holding RA6 low to prevent further data transmission//
while (1)
{
PORTAbits.RA6 = 0;
}
}
Here are the MCC generated config files:
// CONFIG1L
#pragma config RETEN = OFF // VREG Sleep Enable bit->Ultra low-power regulator is Disabled (Controlled by REGSLP bit)
#pragma config INTOSCSEL = HIGH // LF-INTOSC Low-power Enable bit->LF-INTOSC in High-power mode during Sleep
#pragma config SOSCSEL = DIG // SOSC Power Selection and mode Configuration bits->Digital (SCLKI) mode
#pragma config XINST = OFF // Extended Instruction Set->Disabled
// CONFIG1H
#pragma config FOSC = INTIO2 // Oscillator->Internal RC oscillator
#pragma config PLLCFG = OFF // PLL x4 Enable bit->Disabled
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor->Disabled
#pragma config IESO = OFF // Internal External Oscillator Switch Over Mode->Disabled
// CONFIG2L
#pragma config PWRTEN = OFF // Power Up Timer->Disabled
#pragma config BOREN = SBORDIS // Brown Out Detect->Enabled in hardware, SBOREN disabled
#pragma config BORV = 3 // Brown-out Reset Voltage bits->1.8V
#pragma config BORPWR = ZPBORMV // BORMV Power level->ZPBORMV instead of BORMV is selected
// CONFIG2H
#pragma config WDTEN = OFF // Watchdog Timer->WDT disabled in hardware; SWDTEN bit disabled
#pragma config WDTPS = 1048576 // Watchdog Postscaler->1:1048576
// CONFIG3H
#pragma config CANMX = PORTB // ECAN Mux bit->ECAN TX and RX pins are located on RB2 and RB3, respectively
#pragma config MSSPMSK = MSK7 // MSSP address masking->7 Bit address masking mode
#pragma config MCLRE = OFF // Master Clear Enable->MCLR Disabled, RE3 Enabled
// CONFIG4L
#pragma config STVREN = ON // Stack Overflow Reset->Enabled
#pragma config BBSIZ = BB2K // Boot Block Size->2K word Boot Block size
// CONFIG5L
#pragma config CP0 = OFF // Code Protect 00800-03FFF->Disabled
#pragma config CP1 = OFF // Code Protect 04000-07FFF->Disabled
#pragma config CP2 = OFF // Code Protect 08000-0BFFF->Disabled
#pragma config CP3 = OFF // Code Protect 0C000-0FFFF->Disabled
// CONFIG5H
#pragma config CPB = OFF // Code Protect Boot->Disabled
#pragma config CPD = OFF // Data EE Read Protect->Disabled
// CONFIG6L
#pragma config WRT0 = OFF // Table Write Protect 00800-03FFF->Disabled
#pragma config WRT1 = OFF // Table Write Protect 04000-07FFF->Disabled
#pragma config WRT2 = OFF // Table Write Protect 08000-0BFFF->Disabled
#pragma config WRT3 = OFF // Table Write Protect 0C000-0FFFF->Disabled
// CONFIG6H
#pragma config WRTC = OFF // Config. Write Protect->Disabled
#pragma config WRTB = OFF // Table Write Protect Boot->Disabled
#pragma config WRTD = OFF // Data EE Write Protect->Disabled
// CONFIG7L
#pragma config EBTR0 = OFF // Table Read Protect 00800-03FFF->Disabled
#pragma config EBTR1 = OFF // Table Read Protect 04000-07FFF->Disabled
#pragma config EBTR2 = OFF // Table Read Protect 08000-0BFFF->Disabled
#pragma config EBTR3 = OFF // Table Read Protect 0C000-0FFFF->Disabled
// CONFIG7H
#pragma config EBTRB = OFF // Table Read Protect Boot->Disabled
The error is actually in the line just above LATA5 = 0;. The macro __delay_ms is defined in pic18.h as:
_delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
and requires a statement terminator. You are missing a semicolon. Change the line to:
__delay_ms(10);
I want to configure a sensor over the I2C bus using the I2C-dev module.
The required I2C bus is up and running, however, I cannot seem to receive any data from the sensor. Could anyone please help me debug the below code. All the sensor registers are 8 bit.
int main()
{
int devFile=0;
const char *devFileName="/dev/i2c-1";
char writeBuf[2];
uint16_t readBuf[2];
uint16_t tempReading = 0;
/* Initialize I2C interface */
devFile = hdc2010_i2c_init(devFileName, HDC2010_ADDR);
/* Configuring the sensor and trigerring measurement */
writeBuf[0] = HDC2010_CONFIG;
writeBuf[1] = 0x57;
hdc2010_i2c_write(devFile, writeBuf, 2);
writeBuf[0] = HDC2010_INTERRUPT_CONFIG;
writeBuf[1] = 0x78;
hdc2010_i2c_write(devFile, writeBuf, 2);
writeBuf[0] = HDC2010_MEASUREMENT_CONFIG;
writeBuf[1] = 0x03;
hdc2010_i2c_write(devFile, writeBuf, 2);
/* Reading temperature data from the registers */
writeBuf[0] = HDC2010_TEMP_LOW;
hdc2010_i2c_write(devFile, writeBuf, 1);
readBuf[0] = hdc2010_i2c_read(devFile, 1);
writeBuf[0] = HDC2010_TEMP_HIGH;
hdc2010_i2c_write(devFile, writeBuf, 1);
readBuf[1] = hdc2010_i2c_read(devFile, 1);
/*
* Converting the temperature to readable format
* Formula Source : HDC2010 Datasheet
*/
tempReading = ((readBuf[1] << 8) | (readBuf[0]));
tempReading = ((tempReading/65536)*165)-40;
printf("\nTemp: %d\n",tempReading);
}
int hdc2010_i2c_init(const char *devFileName, int slaveAddr)
{
int devFile;
/* Opening I2C device file */
devFile=open(devFileName,O_RDWR);
if (devFile < 0)
{
printf("\nError opening the %s device file.\n",devFileName);
exit (1);
}
/* Selecting HDC2010 by its slave address */
if (ioctl(devFile,I2C_SLAVE,slaveAddr) < 0)
{
printf("\nFailed to select HDC2010(addr=%u)\n",HDC2010_ADDR);
exit (1);
}
return devFile;
}
void hdc2010_i2c_write(int devFile, char *buf, int numBytes)
{
write(devFile, buf, numBytes);
}
uint16_t hdc2010_i2c_read(int devFile, int numBytes)
{
uint16_t readBuf;
read(devFile, &readBuf, 1);
return readBuf;
}
Do I need to use SMBus commands or read/write is sufficient ?
Are there any test applications, like in the case of SPIdev ?
I don't know interface to your chip. There is a great range of possible ways to use I2C. But there is a very common way to access a device with 8-bit registers, so let's assume that is what you need.
To read a register, you want to generate the (simplified) primitive I2C sequence:
Start I2CAddr+Write RegAddr Start I2CAddr+Read [DATA] Stop
But what you are doing is this:
Start I2CAddr+Write RegAddr Stop
Start I2CAddr+Read [DATA] Stop
Basically, you need the read register operation to be a single transaction with one stop at the end and a repeated start between write mode and read mode. But what you are sending is two transactions.
You should not be using the read()/write() interface to i2c-dev. This interface is very simple and not suitable for most I2C transactions. Instead use the ioctl() interface and I2C_RDWR. This allows the appropriate transactions to be generated.
Since certain forms of transactions are very common, including the ones you most likely want, there is a library that has them coded already. Use i2c_smbus_read_byte_data() and i2c_smbus_write_byte_data() from the library in i2c-tools.
As for test programs, there is i2cget and i2cset, part of the above mentioned i2c-tools, that will be able to do what you want.
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)