Posted on Leave a comment

How to connect STM32F429I-DISC1 board to DS1307 using I2C

On the STM32F429 board, there is one I2c extension connector. This connector has eight pins. Which is used to connect to other I2C peripherals such as RTC, EEPROM and other microcontrollers etc.

DS1307 connected to I2C

In the hardware schematics, it is labelled as ACP/RF E2P connector.

The I2C3 SDA and SCL lines are pulled up via a 4.7 k ohm resistor to VCC 3.3V.

This is the basic code that I used to set/get the data in/from the DS1307 via I2c.

/* USER CODE BEGIN 4 */
struct Time{
	  uint8_t sec;
	  uint8_t min;
	  uint8_t hour;
	  uint8_t weekday;
	  uint8_t day;
	  uint8_t month;
	  uint8_t year;
	  };

/* USER CODE END 4 */

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* init code for USB_HOST */
  MX_USB_HOST_Init();
  /* USER CODE BEGIN 5 */
 char buff[30];
  uint8_t *ptr1;
  uint8_t *ptr2;
  ptr2 = (uint8_t *)buff;

  struct Time Set_time,Get_Time;

  Set_time.sec = 0;
  Set_time.min = 0;
  Set_time.hour = 0;
  Set_time.day = 0;
  Set_time.month = 04;
  Set_time.year = 0;
  Set_time.weekday = 0;

  HAL_I2C_Mem_Write(&hi2c3, 0xd0, 0, 1,&Set_time.sec, 7, 1000);



  /* Infinite loop */
  for(;;)
  {
	 
	  HAL_I2C_Mem_Read(&hi2c3, 0xD1, 0, 1, &Get_Time.sec, 7, 1000);
    osDelay(1000);
    ptr1 = (uint8_t *)"Hello\n";
    HAL_UART_Transmit(&huart1, ptr1, 6, 1000);
    sprintf(buff,"%02x:%02x:%02x - %02x - %02x/%02x/%02x \n",
    		Get_Time.hour,
			Get_Time.min,
			Get_Time.sec,
			Get_Time.weekday,
			Get_Time.day,
			Get_Time.month,
			Get_Time.year);
    HAL_UART_Transmit(&huart1, ptr2,26, 1000);
  }
  /* USER CODE END 5 */
}

In this code, I created a structure for the time, weekday and date. Which is similar to the internal registers of DS1307.

The Structure then creates two instances called set_time and Get_time. The Set_time object is filled with the values and then its location is transfered to the HAL_I2C_Mem_Write function. Which sends this data through polling to the DS1307.

Similarly the Get_time structure is used to retrieve the data from the DS1307 using the HAL_I2C_Mem_Read function. Which reads 7 bytes from the DS1307.

The retrieved time and date are then sent via the UART to display on a terminal.

Posted on Leave a comment

How to use DS1307 RTC with ATmega328PB via I2C in Microchip Studio

The DS1307 Real Time Clock uses I2c communication lines to connect with the microcontroller.

I2C uses two lines commonly known as Serial Data/Address or SDA and Serial Clock Line or SCL. The two lines SDA and SCL are standardised and they are implemented using either an open collector or open drain configuration. What this means is that you need to pull these lines UP to VCC. For complete information on how the i2C is implemented in ATmega328PB, you need to go through the section of the datasheet called TWI or Two-Wire Serial Interface.

To start I2C in ATmega328PB, first the SCL frequency needs to set which must be under 100KHz .

To set the SCL frequency you set two registers TWBR0 and TWSR0.

TWSR0 has two bit 0 and bit 1; which sets the prescaler for the clock to the TWI.

Then TWBR0 needs to be set which can anything from 0 to 255.

THen you need to write the I2C functions for start, repeated start, data trasmission and recepetion and stop.

/*
 * main.c
 *
 * Created: 8/20/2022 2:08:09 PM
 *  Author: abhay
 */ 
#define F_CPU 16000000
#include <xc.h>
#include <avr/interrupt.h>

#include <stdio.h>
#include "util/delay.h"
#include "uart.h"


#define Device_Write_address	0xD0				/* Define RTC DS1307 slave address for write operation */
#define Device_Read_address		0xD1				/* Make LSB bit high of slave address for read operation */
#define TimeFormat12			0x40				/* Define 12 hour format */
#define AMPM					0x20

int second,minute,hour,day,date,month,year;

void TWI_init_master(void) // Function to initialize master
{
	TWBR0=127;    // Bit rate
	TWSR0= (1<<TWPS1)|(1<<TWPS0);    // Setting prescalar bits
	// SCL freq= F_CPU/(16+2(TWBR).4^TWPS)
}


								
uint8_t  I2C_Start(char write_address);			/* I2C start function */
uint8_t  I2C_Repeated_Start(char read_address);	/* I2C repeated start function */
void I2C_Stop();								/* I2C stop function */
void I2C_Start_Wait(char write_address);		/* I2C start wait function */
uint8_t  I2C_Write(char data);					/* I2C write function */
int I2C_Read_Ack();							/* I2C read ack function */
int I2C_Read_Nack();							/* I2C read nack function */

void RTC_Read_Clock(char read_clock_address)
{
	I2C_Start(Device_Write_address);				/* Start I2C communication with RTC */
	I2C_Write(read_clock_address);					/* Write address to read */
	I2C_Repeated_Start(Device_Read_address);		/* Repeated start with device read address */

	second = I2C_Read_Ack();						/* Read second */
	minute = I2C_Read_Ack();						/* Read minute */
	hour = I2C_Read_Nack();							/* Read hour with Nack */
	I2C_Stop();										/* Stop i2C communication */
}

void RTC_Read_Calendar(char read_calendar_address)
{
	I2C_Start(Device_Write_address);
	I2C_Write(read_calendar_address);
	I2C_Repeated_Start(Device_Read_address);

	day = I2C_Read_Ack();							/* Read day */
	date = I2C_Read_Ack();							/* Read date */
	month = I2C_Read_Ack();							/* Read month */
	year = I2C_Read_Nack();							/* Read the year with Nack */
	I2C_Stop();										/* Stop i2C communication */
}

int main(void)
{
	char buffer[20];
	const char* days[7]= {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
	UART_Init();
	TWI_init_master();
	sei();
	
	I2C_Start(Device_Write_address);				/* Start I2C communication with RTC */
	I2C_Write(0);					/* Write address to read */
	I2C_Write(0x00);	//sec
	I2C_Write(0x00);	//min			/* Write address to read */
	I2C_Write(0x17);	//hour
	I2C_Write(0x03);	//tuesday
	I2C_Write(0x23);	//day
	I2C_Write(0x09);	//month
	I2C_Write(0x21);	//year
	I2C_Stop();										/* Stop i2C communication */
	

 

    
	while(1)
    {
        //TODO:: Please write your application code 
		RTC_Read_Clock(0);
		//UART_Transmit(second);
		sprintf(buffer, "\n%02x:%02x:%02x  ", (hour & 0b00011111), minute, second);
		UART_SendString(buffer);
		RTC_Read_Calendar(3);
		sprintf(buffer, "%02x/%02x/%02x %s", date, month, year,days[day-1]);
		UART_SendString(buffer);
		_delay_ms(1000);
    }
}

uint8_t I2C_Start(char write_address)						/* I2C start function */
{
	uint8_t status;											/* Declare variable */
	TWCR0 = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);					/* Enable TWI, generate start condition and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (start condition) */
	status = TWSR0 & 0xF8;									/* Read TWI status register with masking lower three bits */
	if (status != 0x08)										/* Check weather start condition transmitted successfully or not? */
	return 0;												/* If not then return 0 to indicate start condition fail */
	TWDR0 = write_address;									/* If yes then write SLA+W in TWI data register */
	TWCR0 = (1<<TWEN)|(1<<TWINT);							/* Enable TWI and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (Write operation) */
	status = TWSR0 & 0xF8;									/* Read TWI status register with masking lower three bits */
	if (status == 0x18)										/* Check weather SLA+W transmitted & ack received or not? */
	return 1;												/* If yes then return 1 to indicate ack received i.e. ready to accept data byte */
	if (status == 0x20)										/* Check weather SLA+W transmitted & nack received or not? */
	return 2;												/* If yes then return 2 to indicate nack received i.e. device is busy */
	else
	return 3;												/* Else return 3 to indicate SLA+W failed */
}

uint8_t I2C_Repeated_Start(char read_address)				/* I2C repeated start function */
{
	uint8_t status;											/* Declare variable */
	TWCR0 = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);					/* Enable TWI, generate start condition and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (start condition) */
	status = TWSR0 & 0xF8;									/* Read TWI status register with masking lower three bits */
	if (status != 0x10)										/* Check weather repeated start condition transmitted successfully or not? */
	return 0;												/* If no then return 0 to indicate repeated start condition fail */
	TWDR0 = read_address;									/* If yes then write SLA+R in TWI data register */
	TWCR0 = (1<<TWEN)|(1<<TWINT);							/* Enable TWI and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (Write operation) */
	status = TWSR0 & 0xF8;									/* Read TWI status register with masking lower three bits */
	if (status == 0x40)										/* Check weather SLA+R transmitted & ack received or not? */
	return 1;												/* If yes then return 1 to indicate ack received */
	if (status == 0x20)										/* Check weather SLA+R transmitted & nack received or not? */
	return 2;												/* If yes then return 2 to indicate nack received i.e. device is busy */
	else
	return 3;												/* Else return 3 to indicate SLA+W failed */
}

void I2C_Stop()												/* I2C stop function */
{
	TWCR0=(1<<TWSTO)|(1<<TWINT)|(1<<TWEN);					/* Enable TWI, generate stop condition and clear interrupt flag */
	while(TWCR0 & (1<<TWSTO));								/* Wait until stop condition execution */
}

void I2C_Start_Wait(char write_address)						/* I2C start wait function */
{
	uint8_t status;											/* Declare variable */
	while (1)
	{
		TWCR0 = (1<<TWSTA)|(1<<TWEN)|(1<<TWINT);				/* Enable TWI, generate start condition and clear interrupt flag */
		while (!(TWCR0 & (1<<TWINT)));						/* Wait until TWI finish its current job (start condition) */
		status = TWSR0 & 0xF8;								/* Read TWI status register with masking lower three bits */
		if (status != 0x08)									/* Check weather start condition transmitted successfully or not? */
		continue;											/* If no then continue with start loop again */
		TWDR0 = write_address;								/* If yes then write SLA+W in TWI data register */
		TWCR0 = (1<<TWEN)|(1<<TWINT);						/* Enable TWI and clear interrupt flag */
		while (!(TWCR0 & (1<<TWINT)));						/* Wait until TWI finish its current job (Write operation) */
		status = TWSR0 & 0xF8;								/* Read TWI status register with masking lower three bits */
		if (status != 0x18 )								/* Check weather SLA+W transmitted & ack received or not? */
		{
			I2C_Stop();										/* If not then generate stop condition */
			continue;										/* continue with start loop again */
		}
		break;												/* If yes then break loop */
	}
}

uint8_t I2C_Write(char data)								/* I2C write function */
{
	uint8_t status;											/* Declare variable */
	TWDR0 = data;											/* Copy data in TWI data register */
	TWCR0 = (1<<TWEN)|(1<<TWINT);							/* Enable TWI and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (Write operation) */
	status = TWSR0 & 0xF8;									/* Read TWI status register with masking lower three bits */
	if (status == 0x28)										/* Check weather data transmitted & ack received or not? */
	return 0;												/* If yes then return 0 to indicate ack received */
	if (status == 0x30)										/* Check weather data transmitted & nack received or not? */
	return 1;												/* If yes then return 1 to indicate nack received */
	else
	return 2;												/* Else return 2 to indicate data transmission failed */
}

int I2C_Read_Ack()											/* I2C read ack function */
{
	TWCR0=(1<<TWEN)|(1<<TWINT)|(1<<TWEA);					/* Enable TWI, generation of ack and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (read operation) */
	return TWDR0;											/* Return received data */
}

int I2C_Read_Nack()										/* I2C read nack function */
{
	TWCR0=(1<<TWEN)|(1<<TWINT);								/* Enable TWI and clear interrupt flag */
	while (!(TWCR0 & (1<<TWINT)));							/* Wait until TWI finish its current job (read operation) */
	return TWDR0;											/* Return received data */
}
Posted on Leave a comment

General Timer based on RTC using stm32f103rb

This timer uses stm32 internal rtc peripheral to display time.

The initialization code is generated using CUBEMX which is embedded inside the CUBE IDE.

/* USER CODE BEGIN WHILE */
	
	RTC_TimeTypeDef readTime;	// RTC Time structure
	RTC_DateTypeDef readDate;	// RTC Date structure
	uint8_t time_hou_var[2];
	uint8_t time_min_var[2];
	uint8_t time_sec_var[2];

	while (1)
	{


    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		/*  function to read time from RTC shadow register */
		    HAL_RTC_GetTime(&hrtc, &readTime, RTC_FORMAT_BIN);

		/* function to read date from RTC shadow register	*/
		    HAL_RTC_GetDate(&hrtc, &readDate, RTC_FORMAT_BIN);

		    itoa(readTime.Hours,(char *)time_hou_var,10);
		    textPtr = ((uint8_t *)(time_hou_var));
		    LCD_Fill(0, 40, 240, (40 + 1)+30, WHITE);
		    WriteString(10,(40*1)+10,textPtr,RED);

		    textPtr = ((uint8_t *)":");
		    WriteString(40,(40*1)+10,textPtr,GREEN);

		    itoa(readTime.Minutes,(char *)time_min_var,10);
		    textPtr = ((uint8_t *)(time_min_var));
		  //  LCD_Fill(0, 40, 240, (40 + 1)+30, WHITE);
		    WriteString(45,(40*1)+10,textPtr,RED);

		    textPtr = ((uint8_t *)":");
		    WriteString(70,(40*1)+10,textPtr,GREEN);

		    itoa(readTime.Seconds,(char *)time_sec_var,10);
		    textPtr = ((uint8_t *)(time_sec_var));
		    //   LCD_Fill(0, 40, 240, (40 + 1)+30, WHITE);
		    WriteString(75,(40*1)+10,textPtr,RED);

		    HAL_Delay(1000);

		    if(HAL_GPIO_ReadPin (GPIOA, KEY1_Pin))
		    {
		    	// Set The LED ON!
		    	HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_SET);

		    }
		    else
		    {
		    	// Else .. Turn LED OFF!
		    	HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_RESET);
		    	//HAL_GPIO_WritePin(LCD_BL_EN_GPIO_Port, LCD_BL_EN_Pin, GPIO_PIN_RESET);
		    	HAL_GPIO_TogglePin(LCD_BL_EN_GPIO_Port, LCD_BL_EN_Pin);
		    }
}

The code uses itoa() function which needs stdlib.h header file.

itoa() function in C language converts the integer into ASCII digits which are stored in a buffer.

itoa( integar_to_be_converted, Buffer_to_store_conversion, Base_system);

You can choose the base system in itoa function. For conversion to decimal number system, you enter 10

for binary, you can write 2.

for hexadecimal, you can write 16.

and so on.

Posted on Leave a comment

Up Counting Timer using STM32L476

I have created this Upcounting timer using the RTC of STM32L476vgt. In this timer, time will keep on incrementing and it will be displayed using the onboard LCD.

How to Read RTC of STM32L476G-DISCO

Here is the code that I have used to make this.

I have used STM32 CUBE IDE for programming and debugging purposes.

  MX_RTC_Init();				// RTC initalization and configuration created using integrated cube mx
  /* USER CODE BEGIN 2 */
  BSP_LCD_GLASS_Init();
  BSP_LCD_GLASS_Clear();
  BSP_LCD_GLASS_DisplayString((uint8_t *)"HALLO");
  HAL_Delay(1000);
  BSP_LCD_GLASS_DisplayString((uint8_t *)"EXASUB");
  HAL_Delay(2000);


  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  RTC_TimeTypeDef readTime;	// RTC Time structure
  RTC_DateTypeDef readDate;	// RTC Date structure
  while (1)
  {
    /* USER CODE END WHILE */
    MX_USB_HOST_Process();

    /* USER CODE BEGIN 3 */

    char bufSec[2];
    char bufMin[2];
    char bufHou[2];
    HAL_RTC_GetTime(&hrtc, &readTime, RTC_FORMAT_BIN);	// function to read time from RTC shadow register
    HAL_RTC_GetDate(&hrtc, &readDate, RTC_FORMAT_BIN);	// function to read date from RTC shadow register
   itoa(readTime.Seconds,bufSec,10);
   itoa(readTime.Minutes,bufMin,10);
   itoa(readTime.Hours,bufHou,10);
   if(readTime.Seconds == 0){
	   BSP_LCD_GLASS_Clear();
   }
   if(readTime.Minutes == 0){
	   BSP_LCD_GLASS_Clear();
   }
   /*
    if(readTime.Hours == 0){
   	   BSP_LCD_GLASS_Clear();
      }
   */
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufHou[0], POINT_OFF, DOUBLEPOINT_OFF, 0);
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufHou[1], POINT_OFF, DOUBLEPOINT_ON, 1);
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufMin[0], POINT_OFF, DOUBLEPOINT_OFF, 2);
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufMin[1], POINT_OFF, DOUBLEPOINT_ON, 3);
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufSec[0], POINT_OFF, DOUBLEPOINT_OFF, 4);
   BSP_LCD_GLASS_DisplayChar((uint8_t *)&bufSec[1], POINT_OFF, DOUBLEPOINT_OFF, 5);

    HAL_Delay(1000);									// HAL Delay of 1000 millisecond
  }
  /* USER CODE END 3 */
}

Posted on 1 Comment

How to Read RTC of STM32L476G-DISCO

Create a Project in STM32 CUBE IDE for the STM32L476G-DISCO board.

Select LSE as Clock Source for RTC

The default option is LSI which uses an internal RC circuit.

LSE is the external 32KHz crystal that is provided on the board for the RTC.

Activate the Clock source and Calendar

This will enable the RTC clock source and will also enable the calendar for date and timekeeping.

If you want to set some default time and date you can set it in the RTC configuration menu. You can always change them later through your code.

After doing the above-said steps you can now generate code. This will give you an initialization code.

To read the value from the RTC registers. You need to remember the following thing.

From Section 38.3.8 of RM0351 Rev 1 Reference Manual

It says that reading the sub-second register or Time register alone will lock the value. It will remain locked until the Date register is read.

To prevent the lockup from happening. It is suggested to read time and date simultaneously one after the another.

Here I am writing working code

 MX_RTC_Init();				// RTC initalization and configuration created using integrated cube mx 
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  RTC_TimeTypeDef readTime;	// RTC Time structure 
  RTC_DateTypeDef readDate;	// RTC Date structure
  while (1)
  {
    /* USER CODE END WHILE */
    

    /* USER CODE BEGIN 3 */
/*  function to read time from RTC shadow register */
    HAL_RTC_GetTime(&hrtc, &readTime, RTC_FORMAT_BIN);

/* function to read date from RTC shadow register	*/
    HAL_RTC_GetDate(&hrtc, &readDate, RTC_FORMAT_BIN);

    printf("Minute: %d\t",readTime.Minutes);
    printf("Seconds: %d\n",readTime.Seconds);

    HAL_Delay(1000);	// HAL Delay of 1000 millisecond
  }
  /* USER CODE END 3 */
}

I am using print statements for debugging using stm32cube ide.
How to use printf using serial wire debug on STM32L476 Discovery