Trouble writing data to DS1337 RTC IC using only Wire library

Hi, I'm new to I2C communication. I've connected DS1337 I2C pins (with pullup resistors) to ATMega328PB. Using code below I can retrieve data from RTC but I'm having problems writing data to it. I want to use only Wire library and avoid IC specific ones.

Data read:

  Wire.beginTransmission(RTC_ADDR);
  Wire.write(address);
  Wire.endTransmission();
  Wire.requestFrom(RTC_ADDR,1);
  if (Wire.available()) {
    data_bcd = Wire.read();
  }

Data write:

  Wire.beginTransmission(RTC_ADDR);
  Wire.write(address);
  Wire.write(value);
  Wire.endTransmission();

And yes I know this RTC is using BCD format.
This fragment does not write data to RTC but I don't see why I wouldn't.

One more thing: I'm calling the function to read data every 5ms by timer interrupt. Could the data avaliable to be send by RTC preventing writing?

For this and other reasons it is preferred that you provide a complete it compiles and we could run it ourselves sketch.

Either the actual sketch you are wrangling, or a reduced sketch that illustrates the problem.

Here's two functions I use alla time:

unsigned char peekRTC(unsigned char regAddress)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(regAddress); // set DS3231 register pointer
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 1);
  
  return (bcdToDec(Wire.read()));
}

void pokeRTC(unsigned char regAddress, unsigned char value)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(regAddress);

  Wire.write(decToBcd(value));
  Wire.endTransmission();
}

Which you can see only has one small difference to what you do. I do not condition reading from the wire on a check with anything being "available" there - looks like it just assumes there will be...

HTH

a7

When you call requestFrom(), it will read in the number of bytes you requested, regardless of the number the slave actually sends. Generally it would be better to check for a return value of 0 from requestFrom(), which would indicate a timeout or other error and no data received.

1 Like

Noted, THX.

a7

The Arduino Wire library is interrupt-driven, you may not use it in a interrupt routine. Every 5ms is also too much. You only need to sync the time every minute or so.

1 Like

Ok so I partly fixed the problem, the else if statements were not in right order in my ISR function. Right now pushing the button just stops the program probably becouse of this fact that I didn't know about:

The Arduino Wire library is interrupt-driven, you may not use it in a interrupt routine.

Thank you for the input and also I agree with alto777 that I provided too little context. I'm providing my simplified (or maybe not so much) code below.

#include <Wire.h>

#define DIGITS_COUNT    5
#define RTC_ADDR        0x68
#define HOUR_ADDR       0x02

//Current digit position for display
uint8_t position = 0;
//Current digits array with digit number and dot point status
uint8_t display_array[DIGITS_COUNT][2];
//Position lookup table for PORT manipulation
const uint8_t position_array[DIGITS_COUNT] = {0b00000001, 0b00000010, 0b00000100, 0b01000000, 0b10000000};  //CA1,CA2,CA3,CA4,CA5
//Digit segment configuration for PORT manipulation
const uint8_t digits_array[11] = {0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b00000000};  //0,1,2,3,4,5,6,7,8,9,null

ISR(TIMER1_COMPA_vect) {
  display_write();
}

ISR(PCINT1_vect) {
  if (!(PINC & 0b00000010) & write_mode) { //If button was pressed (detect LOW state)
    rtc_write(HOUR_ADDR,1); //test
  }
}

void setup() {
  //Set Timer 1 to interrupt and refresh screen periodacly
  TCCR1A  = 0b00000000; 
  TCCR1B  = 0b00001001; //No prescaler, CTC for OCR1A
  TCNT1   = 0;          //Reset initial value
  TIMSK1 |= 0b00000010; //Set interrupt on compare A
  OCR1A   = 5000;       //Display new digit every 5ms

  //Set D and B pins as outputs for driving the display
  DDRD =  0b11111111;
  DDRB |= 0b11000111;

  //Set C pin as input for switch and enable pullup resistor
  DDRC &= 0b11111101;
  PORTC |= 0b00000010;
  //Initialize Pin Chnage Interrupts
  PCICR |= 0b00000010; //Enable PCI for bank C
  PCIFR |= 0b00000111; //Clear interrupt flags
  PCMSK1 |= 0b00000010; //Enable PCI for PC1

  //Initialize I2C communication library
  Wire.begin();
  //Enable external interrupts
  sei();
}

void loop() {
    //Set dot positions
    display_array[0][1] = 0;
    display_array[1][1] = 1;
    display_array[2][1] = 0;
    display_array[3][1] = 1;
    display_array[4][1] = 0;
    rtc_read(HOUR_ADDR, 0, 1);
}

//Read data from RTC and insert digits to positions in display array
void rtc_read(uint8_t address, uint8_t position1, uint8_t position2) {
  uint8_t data_bcd;
  uint8_t data_dec;
  Wire.beginTransmission(RTC_ADDR);
  Wire.write(address);
  Wire.endTransmission();
  Wire.requestFrom(RTC_ADDR,1);
  if (Wire.available()) {
    data_bcd = Wire.read();
  }
  data_dec = (data_bcd / 16 * 10) + (data_bcd % 16);  //Convert RTS's BCD format to decimal
  display_array[position1][0] = data_dec / 10;      //Write most siginificant digit to position 1
  if (position2 < DIGITS_COUNT) {
  display_array[position2][0] = data_dec % 10;      //Write least siginificant digit to position 2
  }
}

//Write data to RTC
void rtc_write(uint8_t address, uint8_t value) {
  Wire.beginTransmission(RTC_ADDR);
  Wire.write(address);
  Wire.write((value / 10*16) + (value % 10));
  Wire.endTransmission();
}

//Display array function
void display_write() {
  if (display_array[position][0] < 11) {
    PORTB &=  0b00111000;                                 //Reset B pins
    PORTD = digits_array[display_array[position][0]];     //Set segment configuration from digits_array look-up table
    PORTD |= (display_array[position][1] << PD7);         //Add dot point bit status to current digit
    PORTB |= position_array[position];                    //Add bit corresponding to position argument from position_array look-up table
    position++;                                           //Move to next digit position
    if (position == DIGITS_COUNT) {                       //Reset digit counter when last position is reached
      position = 0;                                 
    }
  }
}

Context: There is a debounced pushbuttong pulling pin PC1 to ground when pressed. I want this buttong to act as an interrupt and use write function to write some data to RTC when it's pressed. The code is in it's first stages and it's my forst big project with ATMega and I'm using it as a bare-metal MCU soldered on PCB. Banks B and D are driving the segments and cathodes of 5 digit 7-segment display. I want to ultimetly write it in pure C in Microchip Studio using only standard avr libraries.
Currently as I said pushing the button just stops the program - digits stop refreshing. If Wire library is interrupt driven is it better idea to just try to write simplified I2C communication functions from scratch?

Why?

Why do you want this to be an interrupt?

Why isn't it enough to simply have the interrupt routine flag that the button has been pressed, and then handle the data being written the next time you go through loop() ?

Let's take a step back for a moment.

What are you trying to do here?

What are you trying to make?

Once you are finished with your project, what exactly is your project supposed to do?

What leads you to believe that this is not an XY problem?
https://xyproblem.info/

1 Like

@jacksee - what @odometer said, changed to the declarative. Not a question, just do that.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.