stm32l4 RTC HAL not working - c

I'm having a strange behavior with the RTC on a stm32L476 with FreeRTOS.
It only reads the first time in RUN mode, RTC is working, because from run to run it saves the value of the internal register and is going up.
Also if I do DEBUG when I put breakpoint at stm32l4xx_hal_rtc.c at line 583:
tmpreg = (uint32_t)(hrtc->Instance->TR & RTC_TR_RESERVED_MASK);
*breakpoint* sTime->Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16);
I can see the tmpreg and TR register how they update, and then when I click jump to next breakpoint witch is the same I saw the display updated.
So why it's not working when normal RUN?
Init code (cube MX generated):
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
/**Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initialize RTC and set the Time and Date
*/
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x32F2){
sTime.Hours = 0;
sTime.Minutes = 0;
sTime.Seconds = 0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 1;
sDate.Year = 0;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0,0x32F2);
}
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
task where clock is readed and printed all this task and functions are at the same menu.c:
void MenuTask(void const *argument){
for(;;){
/*
* Menus
*/
DrawMenu();
osDelay(100);
}
}
void DrawMenu(){
switch(menuTaskStatus){
/* Not important code */
case MENU_INFO:
menuInfoBar();
break;
}
}
I print on the LCD a bar with the clock in the middle
void menuInfoBar(){
//Clock
CheckClock();
if(updateNeeded.Clock){
DrawClock();
updateNeeded.Clock = 0;
}
}
Here is the problematic part, as you can see I have tried a wait for synchro but also didn't work. I have some doubts of how does this syncro and RTC reading works.
void CheckClock(){
RTC_TimeTypeDef timeVar;
// HAL_GPIO_TogglePin(LEDR_GPIO_Port, LEDR_Pin);
// if(HAL_RTC_WaitForSynchro(&hrtc) == HAL_OK){
while(HAL_RTC_GetTime(&hrtc,&timeVar,RTC_FORMAT_BIN)!= HAL_OK);
if(timeVar.Seconds != timeVarAnt.Seconds){
timeVarAnt.Minutes = timeVar.Minutes;
timeVarAnt.Hours = timeVar.Hours;
timeVarAnt.Seconds = timeVar.Seconds;
updateNeeded.Clock = 1;
}
// }
}
Here I only draw the clock on my display
void DrawClock(){
DISP_locate(49,0);
sprintf((char *)stringBuffer,"%02d:%02d:%02d",(int)timeVarAnt.Hours,(int)timeVarAnt.Minutes,(int)timeVarAnt.Seconds);
DISP_puts((char *)stringBuffer);
}
It's possible I can't read the RTC fast as 100ms?
some one could explain to me why is needed a syncronitzation? datasheet explains that if the clock is 7 time faster is ok, I'm using an 80Mhz APB1 clock
some tutorials and examples I've found the do the exact same I do, but they read on the main loop with osDelay() of many values. Is a problem using freeRTOS and reading from a task?
time has nothing to do I've tried with 5s delay and also don't works
Thanks

A little late may be but I ran into the same problem and it turns out that the HAL_GetTime(..) function has a quirk. HAL_RTC_GetDate() must be called after it to unlock the values.
You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values
* in the higher-order calendar shadow registers to ensure consistency between the time and date values.
* Reading RTC current time locks the values in calendar shadow registers until current date is read.
This is written as a note in their documentation as well as in the source code of the RTC HAL driver.
Personally, I believe that ST's guys should've made a HAL_RTC_GetTimeAndDate() and spare us falling in this trap.

struct
{
unsigned TR;
}*rcc = (void *)0x56456546;
typedef struct
{
unsigned su :4;
unsigned st :2;
unsigned :1;
unsigned mu :4;
unsigned mt :2;
unsigned :1;
unsigned hu :4;
unsigned ht :2;
unsigned pm :1;
unsigned :9
}RCC_TR_T;
typedef struct
{
uint8_t seconds, minutes, hours, pm;
}TIME_T;
TIME_T *GetTime(TIME_T *time)
{
RCC_TR_T *tr = (RCC_TR_T *)&rcc -> TR;
time -> hours = tr -> hu + tr -> ht * 10;
time -> minutes = tr -> mu + tr -> mt * 10;
time -> seconds = tr -> su + tr -> st * 10;
time -> pm = tr -> pm;
return time;
}

This Answer don't answers why the ST "hal" don't works, but it solves what I need, that is using RTC.
This is my new CheckClock function:
void CheckClock(){
uint32_t tmpreg = (uint32_t) hrtc.Instance->TR;
/* Fill the structure fields with the read parameters */
timeVar.Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16);
timeVar.Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >>8);
timeVar.Seconds = (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU));
if(timeVar.Seconds != timeVarAnt.Seconds){
HAL_GPIO_TogglePin(LEDR_GPIO_Port, LEDR_Pin);
timeVarAnt.Minutes = timeVar.Minutes;
timeVarAnt.Hours = timeVar.Hours;
timeVarAnt.Seconds = timeVar.Seconds;
updateNeeded.Clock = 1;
}
}
Thanks
EDIT 05/2018 :
void CheckClock(){
uint32_t tmpreg = (uint32_t) hrtc.Instance->TR;
timeVar.Hours = Bcd2ToByte((uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16));
timeVar.Minutes =Bcd2ToByte( (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >>8));
timeVar.Seconds =Bcd2ToByte( (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU)));
if(timeVar.Seconds != timeVarAnt.Seconds){
timeVarAnt.Minutes = timeVar.Minutes;
timeVarAnt.Hours = timeVar.Hours;
timeVarAnt.Seconds = timeVar.Seconds;
IBS.updates.Clock = 1;
}
// }
}

//HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
char xsa[6];
char your_time[9];
sprintf(xsa,"%06x",(uint32_t)hrtc.Instance->TR);
sprintf((char*)your_time,"%c%c:%c%c:%c%c%c",xsa[0],xsa[1],xsa[2],xsa[3],xsa[4],xsa[5],'\0');

Related

UART only transmitting first and last character of string (PIC16F877A simulation through proteus)

I am a beginner with PIC microcontrollers and trying to learn through tutorials and simultaneously implement a project for which I need to program a PIC microcontroller. I have tried 3 different programs for UART transmission found on various tutorials and I am still having the same issue.
When I try to transmit a string, say "abcd", I only get adadadad.... on repeat. What might be the issue? I have checked the baud rates and it is correct. I have tried introducing delay but it doesnot help. Would greatly appreciate any suggestions. The UART transmission function is part of a frequency counter program that counts the frequency when it receives an interrupt and displays it on LCD. The value displayed on LCD is also to be transmitted via UART, but first I am trying to make it work for a random string "abcd". I am using proteus for simulations. Currently using the following functions for transmitting data string:
void UART_send_char(char bt)
{
while(!TXIF); // hold the program till TX buffer is free
TXREG = bt; //Load the transmitter buffer with the received value
}
void UART_send_string(char* st_pt)
{
while(*st_pt) //if there is a char
UART_send_char(*st_pt++); //process it as a byte data
}
Following is my main function:
void main() {
char op[12]; // Display string for ascii converted long.
char opdb[12]; // double buffer to stop flicker.
unsigned long freq = 0; // display frequency value to use.
unsigned short blinkc=0; // blink counter
int i,n,num;
unsigned char letter;
unsigned char test[]="abcd";
init_ports();
init_interrupts();
Lcd_Init ();
Lcd_Cmd ( _LCD_CLEAR );
Lcd_Cmd ( _LCD_CURSOR_OFF );
start_timer_count();
for(;;) {
if (update_LCD) {
INTCON.GIE = 0; // Disable All interrupts.
INTCON.PEIE = 0; // Disable All Extended interrupts.
freq = (st_TMR1L+(st_TMR1H<<8)+(st_TMR1_ovfl<<16));//*1000;
ltoa(freq, op, 10);
n=ltoa(freq, opdb, 10); // Build string in non display buffer
memcpy(op,opdb,n); // Copy digits
memset(&op[n],' ',12-n); // Blank the rest.
LCD_Out(1,1,"FREQ:");
LCD_Out(1,7,op);
UART_send_string("abcd"); //<-----------TRANSMISSION FUNCTION CALLED HERE
update_LCD=0;
TMR1_counter=0;
TMR0_counter=0;
start_timer_count();
}
if (toggle_LED) { // Also check for signal presence at TMR1.
blinkc=~blinkc;
if (blinkc==0) { setBit(PORTD,0); } else { resBit(PORTD,0); }
toggle_LED=0;
if (freq==0) {
for ( i=0;i<12;i++) { op[i]=' ';}
LCD_Out(1,7,op);
}
}
}
}
This is a complete, builds with MPLABX and XC8, application to show the PIC16F877A asynchronous UART working with the Microchip simulation tool:
/*
* File: main.c
* Author: dan1138
* Target: PIC16F877A
* Compiler: XC8 v2.32
* IDE: MPLABX v5.50
*
* Created on July 21, 2021, 1:29 PM
*
* PIC16F877A
* +----------:_:----------+
* VPP -> 1 : MCLR/VPP PGD/RB7 : 40 <> PGD
* <> 2 : RA0/AN0 PGC/RB6 : 39 <> PGC
* <> 3 : RA1/AN1 RB5 : 38 <>
* <> 4 : RA2/AN2 RB4 : 37 <>
* <> 5 : RA3/AN3 RB3 : 36 <>
* <> 6 : RA4 RB2 : 35 <>
* <> 7 : RA5/AN4 RB1 : 34 <>
* <> 8 : RE0/AN5 RB0 : 33 <>
* <> 9 : RE1/AN6 VDD : 32 <- 5v0
* <> 10 : RE2/AN7 VSS : 31 <- GND
* 5v0 -> 11 : VDD RD7 : 30 ->
* GND -> 12 : VSS RD6 : 29 ->
* 20.000MHz -> 13 : OSC1 RD5 : 28 ->
* 20.000MHz <- 14 : OSC2 RD4 : 27 ->
* <> 15 : RC0/SOSCO RX/DT/RC7 : 26 <>
* <> 16 : RC1/SOSCI TX/CK/RC6 : 25 <>
* <> 17 : RC2/CCP1 RC5 : 24 <>
* <> 18 : RC3/SCL SDA/RC4 : 23 <>
* <> 19 : RD0 RD3 : 22 <>
* <> 20 : RD1 RD2 : 21 <>
* +-----------------------:
* DIP-40
*
* Description:
*
* Unit test for the UART transmit output implementation.
*
* Test runs using the MPLABX v5.50 simulator.
*
* Read the Microchip documentation about how to setup the simulator to show UART output.
*
*/
#pragma config FOSC = HS /* Oscillator Selection bits (HS oscillator) */
#pragma config WDTE = OFF /* Watchdog Timer Enable bit (WDT disabled) */
#pragma config PWRTE = OFF /* Power-up Timer Enable bit (PWRT disabled) */
#pragma config BOREN = OFF /* Brown-out Reset Enable bit (BOR disabled) */
#pragma config LVP = OFF /* Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) */
#pragma config CPD = OFF /* Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) */
#pragma config WRT = OFF /* Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) */
#pragma config CP = OFF /* Flash Program Memory Code Protection bit (Code protection off) */
/*
* Include defines for target specific Special Function Registers
*/
#include <xc.h>
/*
* Tell XC8 compiler what frequency this code sets for system oscillator
*/
#define _XTAL_FREQ 20000000UL
/*
* function to convert unsigned long to ASCII string
*/
void ultoa(void * str, unsigned long data, unsigned char radix)
{
char buffer[32];
char * outstr = 0;
unsigned char index;
unsigned char temp;
outstr = (char *)str;
if(outstr)
{
if((radix > 1) && (radix <= 16))
{
index = 0;
do
{
temp = data % radix;
data = data / radix;
temp = temp + '0';
if (temp > '9') temp = temp + ('A'-'9')-1;
buffer[index++] = temp;
} while (data);
do
{
*outstr++ = buffer[--index];
} while(index);
*outstr = 0;
}
}
}
/*
* Initialize UART
*/
void UART_Init(void)
{
/* Disable UART interrupts */
PIE1bits.TXIE = 0;
PIE1bits.RCIE = 0;
/* Turn off USART module */
RCSTA = 0;
TXSTA = 0;
SPBRG = (_XTAL_FREQ/(16UL * 9600UL) - 1);
TXSTAbits.BRGH = 1;
RCSTAbits.CREN = 1; /* Enable continuous receive */
TXSTAbits.TXEN = 1; /* Enables Transmission */
RCSTAbits.SPEN = 1; /* Enables Serial Port */
/*
* Flush UART receive buffer
*/
RCREG;
RCREG;
RCREG;
}
/*
* Send a character to serial interface
*/
void UART_Write(unsigned char data) {
while(!TRMT); /* Wait for buffer to be empty */
TXREG = data;
}
/*
* Send a string of characters to serial interface
*/
void UART_WriteString(char *pBuffer) {
if (pBuffer)
{
while(*pBuffer)
{
UART_Write(*pBuffer++);
}
}
}
/*
* Test if character is available from serial interface
*/
unsigned char UART_Data_Ready( void )
{
return (RCIF!=0?1:0);
}
/*
* Read a character from serial interface
* Returns a zero if successful.
* Returns non-zero on framing error or overrun error.
*/
unsigned char UART_Read(void *data)
{
unsigned char Result;
char * buffer = (char *)data;
Result = 0;
if (PIR1bits.RCIF)
{
unsigned char rxerr = 0;
if (RCSTAbits.OERR) {
rxerr = 1;
RCSTAbits.CREN = 0; /* reset receiver */
RCSTAbits.CREN = 1;
RCREG;
RCREG;
RCREG;
}
if (RCSTAbits.FERR) {
rxerr = 1;
RCREG; /* Discard character with framing error */
}
if (!rxerr) { /* No error detected during reception */
if(buffer) *buffer = RCREG;
Result = 1;
}
}
return Result;
}
/*
* Initialize this PIC
*/
void PIC_Init( void )
{
/* Disable all interrupt sources */
INTCON = 0;
PIE1 = 0;
PIE2 = 0;
/*
* Pull-ups off, INT edge low to high, WDT prescale 1:1
* TMR0 clock edge low to high, TMR0 clock = _XTAL_FREQ/4, TMR0 prescale 1:16
* TIMER0 will assert the overflow flag every 256*16 (4096)
* instruction cycles, with a 20MHz oscillator this is 0.8192 milliseconds.
*/
OPTION_REG = 0b11000011;
/* Make all GPIO pins digital */
CMCON = 0x07;
ADCON1 = 0x06;
}
/*
* Main application
*/
void main(void)
{
char output[40];
unsigned long Count;
/*
* Initialize application
*/
PIC_Init();
UART_Init();
UART_WriteString("PIC16F877A UART test build on " __DATE__ " at " __TIME__ "\r\n");
Count = 0;
/*
* Application process loop
*/
for(;;)
{
ultoa(output,Count,10);
UART_WriteString("Count: ");
UART_WriteString(output);
UART_WriteString("\r\n");
Count++;
}
/*
* Keep XC8 from whining about functions not being called
*/
UART_Data_Ready();
UART_Read(0);
}
I would expect this to work with your Proteus environment too.
It's on you to port this code to your project.

time between two edges pic 18f4550

I would like to calculate the time interval between two rising edges of two different signals using the two CCP modules from pic 18f4550.
The idea of ​​calculation is illustrated in the following figures.
The simulation works fine, but my electrical circuit is not. I don't know if there is something wrong with my code. If anyone has an answer or a clue to fix this, I will be grateful! And if you have any questions, please feel free to ask.
#pragma config FOSC = INTOSC_EC
#define _XTAL_FREQ 8000000
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "LCD_8bit_file.h"
#include <string.h>
unsigned long comtage, capt0, x;
char DEPHASAGE[20];
char pulse[20];
float period, dephTempo, deph, phi;
void main()
{
IRCF0 = 1; /* set internal clock to 8MHz */
IRCF1 = 1;
IRCF2 = 1;
LCD_Init();
LCD_String_xy(0, 1, "Dephasage[rad]");
T3CONbits.RD16 = 1;
T3CKPS0 = 0;
T3CKPS1 = 0;
TMR3CS = 0;
TMR3IF = 0;
while (1)
{
CCP2CON = 0b00000101;
CCP1CON = 0b00000101;
PIR2bits.CCP2IF = 0;
PIR1bits.CCP1IF = 0;
TMR3ON = 0;
TMR3 = 0;
if (PIR1bits.CCP1IF == 1) {
TMR3ON = 1;
while (!PIR2bits.CCP2IF);
comtage = TMR3;
dephTempo = (((float)comtage / 30.518) / 65536);
sprintf(pulse,"%.3f ", dephTempo);
LCD_String_xy(0, 0, "Dephasage : ");
LCD_String_xy(2, 9, pulse);
}
}
}
When you test a schematic using a real circuit, other issues will appear, like capacitive and resistive parasitics and that will after the timings. Also, can have jitter noise. If you a have an oscilloscope, try to figure out if there is too much noise. Try do add a pull-down/pull-up on those lines, make sure you have good ground connection. But after looking for your code, you should take an approach similar to CTC: You make fast samples of your input signal and then you check your sampled array, if there is more one than zeros, you caught an edge trigger.
I have a better scenario for your application to implement. But first let's talk about the bad practices in your code.
In your main while loop you setup the CCP modules:
CCP2CON = 0b00000101;
CCP1CON = 0b00000101;
PIR2bits.CCP2IF = 0;
PIR1bits.CCP1IF = 0;
TMR3ON = 0;
TMR3 = 0;
You better do this before the program enters to the infinite while loop.
You handle the timer reads directly while the CCP module captures its value in which the edge you configured it to capture.
comtage = TMR3;
I don't see that you configure the CCP pins as inputs. You have to configure them as inputs by setting the corresponding TRIS bits in order to have them working properly.
So the structure of my recommended scenario would be something like this:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "LCD_8bit_file.h"
#include <string.h>
unsigned long comtage, capt0, x;
char DEPHASAGE[20];
char pulse[20];
float period, dephTempo, deph, phi;
/******************** Utility funcs ********************/
void setupSysClock() {
IRCF0 = 1; /* set internal clock to 8MHz */
IRCF1 = 1;
IRCF2 = 1;
}
void setupCCP1withTMR1() {
CCP1CON = 0b00000101;
PIR1bits.CCP1IF = 0;
TRISCbits.TRISC2 = 1; // set CCP1 pin as input
}
void setupCCP2withTMR3() {
CCP2CON = 0b00000101;
PIR2bits.CCP2IF = 0;
// T3 goes for CCP2 and CCP1, PS 1:1, internal CS
T3CON = (1 << T3CCP2) | (0 << T3CKPS1) | (0 << T3CKPS0) | (1 << T3CCP1) | (0 << TMR3CS);
TMR3 = 0;
// In config bits you must choose RC1 pin for the CCP2 if necessary although it so by default
TRISCbits.TRISC1 = 1 // set CCP2 pin as input
}
void rearm() {
CCP1CON = 0x5
CCP2CON = 0x5
PIR1bits.CCP1IF = 0;
PIR2bits.CCP2IF = 0;
TMR3 = 0;
TMR3ON = 1;
}
void suspend() {
CCP1CON = 0;
CCP2CON = 0;
}
void main()
{
setupSysClock(); // setu internal clock
setupCCP1withTMR1(); // setup CCP1 for capture mode
setupCCP2withTMR3(); // setup CCP1 for capture mode with TMR3
LCD_Init();
LCD_String_xy(0, 1, "Dephasage[rad]");
while (1)
{
while(!CCP2IF); // Wait for the second rising edge
// Event has occured process, first make sure that the CCP1 rised first
if(!CCP1F) {
// An invalid sequence occured ignore and rearm. Note that the sequence of signals is important.
rearm();
continue;
}
/* The sequence is correct let's process the event. Here you will have
two captured value in CCPR1 and CCPR2 registers. First one is the captured value of the T3 when the first rising event occured. Second one is the captured value of the T3 when the second rising event occured. You have to get the delta of the two captured values first. This delta value is the elapsed ticks between the two discrete rising input signals. This is what the capture hardware is made for ;)
Now first we shuld suspend the CCP modules to avoid unwanted captures
while we process the previous value. Because if another capture occures
before we process the previous value, the new capture value will be
overwritten over the old value that we need for computation.
*/
suspend(); // suspend the CCP modules while processing
uint16_t timeDelta = CCPR2 - CCPR1; // Calculate the difference
dephTempo = (((float)timeDelta / 30.518) / 65536);
sprintf(pulse,"%.3f ", dephTempo);
LCD_String_xy(0, 0, "Dephasage : ");
LCD_String_xy(2, 9, pulse);
// Now that we finished processing we can rearm the CCP for new captures
rearm();
}
}
I wrote this code in an editor and haven't compiled in MPLAB. So you must compile and test the code. You can give me a feedback for me to help further.
One important thing to note: If the amount of time between two signals is large, you must either increment the prescaler of Timer3 or you must use a complementary variable for TMR3 register in case it overflows.

Cannot write to flash memory - STM32L462

I'm trying to flash data to my flash memory by parsing the hex file sent over UART. But after erasing the flash, I can't write to it. This is my code:
Code to erase flash:
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR | FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR |FLASH_FLAG_PGSERR);
/* Get the 1st page to erase */
FirstPage = ADDR_FLASH_PAGE_12;
/* Get the number of pages to erase from 1st page */
NbOfPages = 243;
/* Get the bank */
BankNumber = FLASH_BANK_1;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Banks = BankNumber;
EraseInitStruct.Page = FirstPage;
EraseInitStruct.NbPages = NbOfPages;
HAL_FLASHEx_Erase(&EraseInitStruct,&PAGEError);
Code to write flash:
uint64_t flashData = 0;
uint32_t flashWriteAddress = (extendedAddress | recordAddress | k);
nibbleArrayToUInt64(&data[recordDataOffset],(uint32_t)i); //convert nibble[] to uint64_t and store in flashData
CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,flashWriteAddress,(uint64_t)flashData);
Code to convert nibble array to uint64_t:
void nibbleArrayToUInt64(uint8_t* var, uint32_t lowest_pos)
{
uint8_t byte;
flashData = 0;
for(int j=0;j<16;j+=2)
{
byte = (((uint64_t)(var[lowest_pos+j]<<4 | var[lowest_pos+j+1])));
flashData |= (((uint64_t)byte) << (8*(7-(j/2))));
}
}
The error flags triggered are :
HAL_FLASH_ERROR_PROG
HAL_FLASH_ERROR_PGA
HAL_FLASH_ERROR_PGS
I don't know what mistake I'm doing. Can someone please help me?
You should unlock the flash before erase or write. For example:
HAL_FLASH_Unlock();
FLASH_EraseInitTypeDef pEraseInit;
pEraseInit.Banks = 0;
pEraseInit.NbSectors = 1;
pEraseInit.Sector = FLASH_SECTOR_2;
pEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
pEraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;
uint32_t sectorError;
HAL_FLASHEx_Erase(&pEraseInit, &sectorError);
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, VERSION_SAVE_ADDRESS, version);
HAL_FLASH_Lock();

Confusion regarding #interrupt-cells configuration on PCA9555 expander

I'm trying to setup a device tree source file for the first time on my custom platform. On the board is a NXP PCA9555 gpio expander. I'm attempting to setup node for the device and am a bit confused.
Here is where I'm at with the node in the dts file:
ioexp0: gpio-exp#21 {
compatible = "nxp,pca9555";
reg = <21>;
interrupt-parent = <&gpio>;
interrupts = <8 0>;
gpio-controller;
#gpio-cells = <2>;
/*I don't understand the following two lines*/
interrupt-controller;
#interrupt-cells = <2>;
};
I got to this point by using the armada-388-gp.dts source as a guide.
My confusion is on what code processes the #interrupt-cells property. The bindings documentation is not very helpful at all for this chip as it doesn't say anything regarding interrupt cell interpretation.
Looking at the pca953x_irq_setup function in the source code for the pca9555 driver - I don't see anywhere that the #interrupt-cells property is handled. Is this handled in the linux interrupt handling code? I'm just confused as to how I'm suppose to know the meaning of the two interrupt cells.
pca953x_irq_setup for your convenience:
static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base)
{
struct i2c_client *client = chip->client;
int ret, i;
if (client->irq && irq_base != -1
&& (chip->driver_data & PCA_INT)) {
ret = pca953x_read_regs(chip,
chip->regs->input, chip->irq_stat);
if (ret)
return ret;
/*
* There is no way to know which GPIO line generated the
* interrupt. We have to rely on the previous read for
* this purpose.
*/
for (i = 0; i < NBANK(chip); i++)
chip->irq_stat[i] &= chip->reg_direction[i];
mutex_init(&chip->irq_lock);
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL,
pca953x_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_SHARED,
dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
return ret;
}
ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
&pca953x_irq_chip,
irq_base,
handle_simple_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(&client->dev,
"could not connect irqchip to gpiochip\n");
return ret;
}
gpiochip_set_nested_irqchip(&chip->gpio_chip,
&pca953x_irq_chip,
client->irq);
}
return 0;
}
This is my first time working with device tree so I'm hoping it's something obvious that I'm just missing.
After looking at all of the comments I did some additional reading and figured out my answer.
I now understand that I was misinterpreting some properties of the device tree. I was previously under the impression that the driver had to specify how all properties were handled. I now see that linux will actually handle many of the generic properties such as gpios or interrupts (which makes a lot of sense).
The documentation on the actual interrupts binding was very helpful, not the documentation for the device driver.
Here is a bit more of a detailed explanation of how the translation from intspec to IRQ_TYPE* happens:
The function of_irq_parse_one copies the interrupt specifier integers to a struct of_phandle_args here. This arg is then passed to irq_create_of_mapping via a consumer function (e.g. of_irq_get). This function then maps these args to a struct irq_fwspec via of_phandle_args_to_fwspec and passes it's fwspec data to irq_create_fwspec_mapping. These functions are all found in irqdomain.c. At this point the irq will belong to an irq_domain or use the irq_default_domain. As far I can tell - the pca853x driver uses the default domain. This domain is often setup by platform specific code. I found mine by searching for irq_domain_ops on cross reference. A lot of these seem to do simple copying of intspec[1] & IRQ_TYPE_SENSE_MASK to the type variable in irq_create_fwspec_mapping via irq_domain_translate. From here the type is set to the irq's irq_data via irqd_set_trigger_type.
of_irq_parse_one:
/**
* of_irq_parse_one - Resolve an interrupt for a device
* #device: the device whose interrupt is to be resolved
* #index: index of the interrupt to resolve
* #out_irq: structure of_irq filled by this function
*
* This function resolves an interrupt for a node by walking the interrupt tree,
* finding which interrupt controller node it is attached to, and returning the
* interrupt specifier that can be used to retrieve a Linux IRQ number.
*/
int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{
struct device_node *p;
const __be32 *intspec, *tmp, *addr;
u32 intsize, intlen;
int i, res;
pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
/* OldWorld mac stuff is "special", handle out of line */
if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
return of_irq_parse_oldworld(device, index, out_irq);
/* Get the reg property (if any) */
addr = of_get_property(device, "reg", NULL);
/* Try the new-style interrupts-extended first */
res = of_parse_phandle_with_args(device, "interrupts-extended",
"#interrupt-cells", index, out_irq);
if (!res)
return of_irq_parse_raw(addr, out_irq);
/* Get the interrupts property */
intspec = of_get_property(device, "interrupts", &intlen);
if (intspec == NULL)
return -EINVAL;
intlen /= sizeof(*intspec);
pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
/* Look for the interrupt parent. */
p = of_irq_find_parent(device);
if (p == NULL)
return -EINVAL;
/* Get size of interrupt specifier */
tmp = of_get_property(p, "#interrupt-cells", NULL);
if (tmp == NULL) {
res = -EINVAL;
goto out;
}
intsize = be32_to_cpu(*tmp);
pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
/* Check index */
if ((index + 1) * intsize > intlen) {
res = -EINVAL;
goto out;
}
/* Copy intspec into irq structure */
intspec += index * intsize;
out_irq->np = p;
out_irq->args_count = intsize;
for (i = 0; i < intsize; i++)
out_irq->args[i] = be32_to_cpup(intspec++);
/* Check if there are any interrupt-map translations to process */
res = of_irq_parse_raw(addr, out_irq);
out:
of_node_put(p);
return res;
}
EXPORT_SYMBOL_GPL(of_irq_parse_one)
irq_create_fwspec_mapping:
unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
{
struct irq_domain *domain;
struct irq_data *irq_data;
irq_hw_number_t hwirq;
unsigned int type = IRQ_TYPE_NONE;
int virq;
if (fwspec->fwnode) {
domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);
if (!domain)
domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);
} else {
domain = irq_default_domain;
}
if (!domain) {
pr_warn("no irq domain found for %s !\n",
of_node_full_name(to_of_node(fwspec->fwnode)));
return 0;
}
if (irq_domain_translate(domain, fwspec, &hwirq, &type))
return 0;
/*
* WARN if the irqchip returns a type with bits
* outside the sense mask set and clear these bits.
*/
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
type &= IRQ_TYPE_SENSE_MASK;
/*
* If we've already configured this interrupt,
* don't do it again, or hell will break loose.
*/
virq = irq_find_mapping(domain, hwirq);
if (virq) {
/*
* If the trigger type is not specified or matches the
* current trigger type then we are done so return the
* interrupt number.
*/
if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
return virq;
/*
* If the trigger type has not been set yet, then set
* it now and return the interrupt number.
*/
if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
irq_data = irq_get_irq_data(virq);
if (!irq_data)
return 0;
irqd_set_trigger_type(irq_data, type);
return virq;
}
pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
return 0;
}
if (irq_domain_is_hierarchy(domain)) {
virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
if (virq <= 0)
return 0;
} else {
/* Create mapping */
virq = irq_create_mapping(domain, hwirq);
if (!virq)
return virq;
}
irq_data = irq_get_irq_data(virq);
if (!irq_data) {
if (irq_domain_is_hierarchy(domain))
irq_domain_free_irqs(virq, 1);
else
irq_dispose_mapping(virq);
return 0;
}
/* Store trigger type */
irqd_set_trigger_type(irq_data, type);
return virq;
}
EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);

errors encountered while interfacing eeprom with microcontroller

I am not an expert c programmers and in the c code I m getting these kinds of errors. I got many and tried to sort them out but can not solve these. The code is as follows:
/*
* EEPROM.c
* interfacing microchip 24aa64f IC with atmel sam4e
*/
#include <asf.h>
#include "EEPROM_I2C.h"
#define DEVICE_ADDRESS 0x50 // 7-bit device identifier 0101000, (refer datasheet)
//#define EEPROM_NAME 24AA6F
#define I2C_FAST_MODE_SPEED 400000//TWI_BUS_CLOCK 400KHz
#define TWI_CLK_DIVIDER 2
#define TWI_CLK_DIV_MIN 7
#define TWI_CLK_CALC_ARGU 4
#define TWI_CLK_DIV_MAX 0xFF
/*************************** Main function ******************************/
int eeprom_main( void )
{
struct micro24 ptMicro24 ;
typedef struct twi_options twi_options_t;
typedef struct Twi_registers Twi;
char TxBuffer[128] ;
char RxBuffer[128] ;
int BufferIndex;
unsigned int PageCount;
unsigned int error = 0 ;
unsigned int i;
ptMicro24.PageSize = 32;
ptMicro24.NumOfPage = 128;
ptMicro24.EepromSize = 128*32;
ptMicro24.SlaveAddress = DEVICE_ADDRESS;
ptMicro24.EepromName = 64;
/***************************** CLOCK SETTINGS TO GET 400KHz **********************
* Set the I2C bus speed in conjunction with the clock frequency.
* param p_twi Pointer to a TWI instance.
* return value PASS\Fail New speed setting is accepted\rejected
**********************************************************************************/
uint32_t twi_set_speed(struct Twi_registers *Twi, uint32_t ul_speed, uint32_t ul_mck)
//uint32_t twi_set_speed(Twi *p_twi, uint32_t ul_speed, uint32_t ul_mck)
{
uint32_t ckdiv = 0; //clock divider is used to increase both TWCK high and low periods (16-18)
uint32_t c_lh_div; //CHDIV (0-7) and CLDIV (8-15)
if (ul_speed > I2C_FAST_MODE_SPEED) { //ul_speed is the desired I2C bus speed
return FAIL;
}
c_lh_div = ul_mck / (ul_speed * TWI_CLK_DIVIDER) - TWI_CLK_CALC_ARGU; //ul_mck main clock of the device
/* cldiv must fit in 8 bits, ckdiv must fit in 3 bits */
while ((c_lh_div > TWI_CLK_DIV_MAX) && (ckdiv < TWI_CLK_DIV_MIN))
{
ckdiv++; // Increase clock divider
c_lh_div /= TWI_CLK_DIVIDER; //Divide cldiv value
}
/* set clock waveform generator register */
Twi->TWI_CWGR =
TWI_CWGR_CLDIV(c_lh_div) | TWI_CWGR_CHDIV(c_lh_div) |
TWI_CWGR_CKDIV(ckdiv);
return PASS;
}
/************************************ Initialize TWI master mode ************************
* Set the control register TWI_CR by MSEN and SVDIS
* param p_opt Options for initializing the TWI module
* return TWI_SUCCESS if initialization is complete
* twi_options... structure contains clock speed, master clock, chip and smbus
*****************************************************************************************/
uint32_t twi_master_start(struct Twi_registers *Twi, struct twi_options_t *twi_options_t)
//uint32_t twi_master_start(Twi *p_twi, const twi_options_t *p_opt)
{
uint32_t status = TWI_SUCCESS; // status success return code is 0
// Enable master mode and disable slave mode in TWI_CR
Twi -> TWI_CR_START = TWI_CR_START;
Twi->TWI_CR_MSEN = TWI_CR_MSEN; // Set Master Enable bit
Twi->TWI_CR_SVDIS = TWI_CR_SVDIS; // Set Slave Disable bit
/* Select the speed */
//new//if (twi_set_speed(Twi->TWI_SR, twi_options_t->speed, twi_options_t->master_clk) == FAIL)
//if (twi_set_speed(Twi, twi_options_t->speed, twi_options_t->master_clk) == FAIL)
//{
//status = TWI_INVALID_ARGUMENT; /* The desired speed setting is rejected */
//}
if (twi_options_t->smbus == 0)
{
Twi->TWI_CR_QUICK == 0;
status = TWI_INVALID_ARGUMENT;
}
else
if (twi_options_t->smbus == 1)
{
Twi->TWI_CR_QUICK == 1;
status = TWI_SUCCESS;
}
return status;
}
/***************************** WriteByte Function ********************************
This function uses a two bytes internal address (IADR) along with
Internal word address of eeprom.
Return Value: None
***********************************************************************************/
void WriteByte (struct micro24 *ptMicro24, char Data2Write,
unsigned int Address)
//Data2Write is the data to be written n the eeprom
//struct <micro24 *ptMicro24> : Structure of Microchip 24AA Two-wire Eeprom
//unsigned int Address>: Address where to write
{
unsigned int WordAddress;
unsigned int SlaveAddress;
unsigned char p0=0;
TWI_CR_START ==1;
if (ptMicro24->EepromName == 64 )
{
if ( Address > 0xFFFF)
{
p0 = 1;
/* Mask the 17th bit to get the 16th LSB */
WordAddress = Address & 0xFFFF ;
SlaveAddress = ptMicro24->SlaveAddress + (p0<<16) ;
}
else {
SlaveAddress = ptMicro24->SlaveAddress ;
WordAddress = Address ;
}
}
TWI_CR_STOP ==1;
//TWI_WriteSingleIadr(TWI_IADR_IADR,SlaveAddress, WordAddress,
// TWI_MMR_IADRSZ_2_BYTE, &Data2Write); // declared as extern
// to write to internal address, utilizing internal address and master mode register
//}
/******************** Increase Speed Function *****************************
* TWI is accessed without calling TWI functions
/***************************************************************************/
int NumOfBytes, Count;
int status;
uint32_t Buffer;
/* Enable Master Mode of the TWI */
TWI_CR_MSEN == 1;
// Twi.TWI_CR_MSEN ==1;
//TWI_CR->TWI_CR_MSEN = TWI_CR_MSEN ;
/* Set the TWI Master Mode Register */
Twi->TWI_MMR = (SlaveAddress & (~TWI_MMR_MREAD) | (TWI_MMR_IADRSZ_2_BYTE));
/* Set the internal address to access the wanted page */
Twi -> TWI_IADR = WordAddress ;
/* Wait until TXRDY is high to transmit the next data */
status = TWI_SR_TXRDY;
while (!(status & TWI_SR_TXRDY))
status = TWI_SR_TXRDY;
/* Send the buffer to the page */
for (Count=0; Count < NumOfBytes ;Count++ )
{
Twi ->TWI_THR_TXDATA = Buffer++;
/* Wait until TXRDY is high to transmit the next data */
status = TWI_SR_TXRDY;
while (!(status & TWI_SR_TXRDY))
status = TWI_SR_TXRDY;
}
/* Wait for the Transmit complete is set */
status = TWI_SR_TXCOMP;
while (!(status & TWI_SR_TXCOMP))
status = TWI_SR_TXCOMP;
// add some wait function according to datasheet before sending the next data
// e.g: 10ms
// e.g: WaitMiliSecond (10);
}
/****************************** ReadByte Function **************************
This function uses a two bytes internal address (IADR) along with
Internal word address of eeprom.
Return Value: None
****************************************************************************/
char ReadByte (struct micro24 *ptMicro24,
unsigned int Address) //int Address to read
{
unsigned int WordAddress;
unsigned int SlaveAddress;
char Data2Read ;
unsigned char p0=0;
TWI_CR_START == 1;
//p_twi -> TWI_CR_START = TWI_CR_START;
if (ptMicro24->EepromName == 64)
{
if ( Address > 0xFFFF) {
p0 = 1;
// Mask the 17th bit to get the 16th LSB
WordAddress = Address & 0xFFFF ;
SlaveAddress = ptMicro24->SlaveAddress + (p0<<16) ;
}
else {
SlaveAddress = ptMicro24->SlaveAddress ;
WordAddress = Address ;
}
}
//TWI_ReadSingleIadr(TWI_IADR_IADR,SlaveAddress,WordAddress,
// TWI_MMR_IADRSZ_2_BYTE,&Data2Read);
// declared as extern
// to write to internal address, utilizing internal address and master mode register
return (Data2Read);
}
}
errors are:
(24,19): error: storage size of 'ptMicro24' isn't known
67,5): error: dereferencing pointer to incomplete type
Twi->TWI_CWGR =
error: expected identifier before '(' token
#define TWI_CR_START (0x1u << 0) /**< \brief (TWI_CR) Send a START Condition */
error: expected identifier before '(' token
#define TWI_CR_MSEN (0x1u << 2) /**< \brief (TWI_CR) TWI Master Mode Enabled */
error: expected identifier before '(' token
#define TWI_CR_SVDIS (0x1u << 5) /**< \brief (TWI_CR) TWI Slave Mode Disabled */
error: dereferencing pointer to incomplete type
if (twi_options_t->smbus == 0)
It seems missing the declaration of struct micro24, this may be the cause of first error: error: storage size of 'ptMicro24' isn't known.
The same for declaration of Twi_registers, that is causing other errors.
Either you forgot to declare these structs or to include an header file declaring them.

Resources