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

How to use UART Receive complete ISR of ATmega328PB using microchip studio

When you enable the communication using the UART. You have the flexibility to either use the Polling or Interrupt method to continue with your programming.

Polling halts the execution of the program and waits for the UART peripheral to receive something so that program execution must continue. But it eats a lot of the computing time.

So, Interrupt Service Routine is written and implemented such the program execution does not stop. It will stop when there is an interrupt and when there is data in the UDR0 register of UART. Then the ISR will execute and then transfer the control to the main program. Which saves a lot of computing time.

you have to add an interrupt library in your program.

#include <avr/interrupt.h>

Then you need to enable the Global interrupt flag.

.
.
.
int main()
{
.
.
.
sei();            // This is Set Enable Interryupt

   while(1)
  {
     // This is your application code.
   }

}

Then you need to enable the UART receive complete interrupt. by setting ‘1’ to RXCIE0 bit of USCR0B register.

Write the ISR function which takes “USART0_RX_vect” as the argument.

char Received_char;
ISR(USART0_RX_vect)
{
	Received_char = UDR0;
}

int main()
{
UCSR0B = (1 << RXCIE0)|(1<<RXEN0)|(1<<TXEN0); 
.
.
.
sei();
while(1);
{
}

}

The above code shows you how to implement UART receive complete ISR. It is not a full initialisation code. You still have to write the UBRR and the frame control to enable the uart peripheral.

Posted on Leave a comment

Mouse Double Clicking Problem Solved

I have two PC mice one is Belkin which is bought in 2012, and the other one is HP which I bought in 2019. Both the mouse had a similar problem. Everything was working perfectly fine. But suddenly their left button started behaving in a weird manner. When i click on an object and try to hold the object. The button loses the signal and then it tries again. It also does double clicking. Most of the time it double-clicks whenever you try to do a single click.

I opened the mouse and then i opened the switch housing and just rubbed the contact so that any deposit would be removed. After that, I put the mouse back together. And it started working normally.

You can follow the instruction given in the video below.

Posted on Leave a comment

How to use internal temperature sensor of ATmega328pb

ATmega328PB is a new semiconductor microcontroller from Microchip semiconductors. I have used its previous generation which is ATmega328 and ATmega328P. They were usually found on Arduino Uno and Arduino nano.

This new IC has a temperature sensor built into it. Which is handy for measuring the die temperature. Which can make device stable in high-temperature design. It is not accurate as a dedicated temperature sensor. But it gives you a rough idea. Using this you can the processes.

It is not an Ambient temperature Sensor.

/*
 * main.c
 *
 * Created: 8/15/2022 4:06:41 PM
 *  Author: abhay
 */ 
#define F_CPU 16*1000000
#include <xc.h>
#include "uart.h"
#include "util/delay.h"
#include <stdlib.h>
long Ctemp;
unsigned int Ftemp;

int main(void)
{
		DDRD &= ~(1 << DDD0);							// PD0 - Rx Input
		DDRD |= (1 << DDD1);							// PD1 - Tx Ouput
		USART_Init();
		
	/* Replace with your application code */
	ADMUX = (1<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (0<<MUX2) | (0<<MUX1) | (0<<MUX0);
	ADCSRA =  (1<<ADPS2) |(1<<ADPS1) | (1<<ADEN);
	
	ADCSRA |= (1<<ADSC);
	
	while ((ADCSRA & (1<<ADSC)) !=0);
	
	while (1)
	{
		ADCSRA |= (1<<ADSC);
		while ((ADCSRA & (1<<ADSC)) !=0);
		
		Ctemp = ((ADC - 247)/1.22)*1000;
		Ftemp = (Ctemp * 1.8) + 32;
		
		USART_Transmit( (((int)Ctemp/100000)%10) + 48);
		USART_Transmit( (((int)Ctemp/10000)%10) + 48);
		USART_Transmit( (((int)Ctemp/1000)%10) + 48);
		USART_Transmit('.');
		USART_Transmit( (((int)Ctemp/100)%10) + 48);
		USART_Transmit( (((int)Ctemp/10)%10) + 48);
		USART_Transmit( ((int)Ctemp%10) + 48);
		
		USART_Transmit('\n');
		_delay_ms(1000);
	}
	return -1;
}
Posted on Leave a comment

How to add USBASP as External Tool in Microchip studio

Download microchip studio from here https://www.microchip.com/en-us/tools-resources/develop/microchip-studi

Install the program.

I use USBASP to program AVR ATmega328pb.

Go to Tools > External Tools > ADD

Command: location of avrdude

C:\WinAVR-20100110\bin\avrdude.exe

Arguments:

-c usbasp -p m328pb -U flash:w:$(ProjectDir)Debug\$(TargetName).hex:i

Check the Use Output window to be able to see the output of the avrdude inside the microchip studio terminal.

Posted on Leave a comment

Power Bipolar Transistors are limited by DC operations.

Power transistors are big bulky because they have to deal with a large amount of current in a very short amount of time. They are the fastest of all the transistors.

Heat is the biggest enemy of the transistor. As silicon is heavily dependent on temperature. As the die temperature increases the more sensitive it becomes. And at a certain point, it burns out. At the moment of burning there is a short which generates so much heat that the silicon just burns itself off.

Even the TIP3055 can only handle instantaneous current pulses for a very short amount of time. If that same transistor is put under a continuous load it will burn out in a few instances.

If we look at the derating curve of any power transistor. We will find out that they are only good for up to 1Amps of DC operation. And even at 1 Amp, it will need a sizeable amount of aluminium heatsink.

Posted on Leave a comment

How to set up UART of ATmega328pb in Atmel Studio 7.0

To set up uart in Atmel studio 7.0.

Firstly you will need a common baud rate.

Then you go to section 24.11 of the datasheet. You will find common calculated values for the UBRRn register.

UBRRn register is comprised of high and low registers.

First, you have to initialise the Data direction registers for the RX and Tx Pins. Then you initialise the UART peripheral.

DDRD &= ~(1 << DDD0);				// PD0 - Rx Input
DDRD |= (1 << DDD1);				// PD1 - Tx Ouput
USART_Init();					// UART intialise

Here is the basic UART library code.

/*
* Name: UART library Code
*/
void USART_Init( )
{
	/*Set baud rate */
	
	UBRR0L = 103;
	/* Enable receiver and transmitter */
	UCSR0B = (1 << RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
	/* Set frame format: 8data, 1stop bit */
	UCSR0C = (3<<UCSZ00);
}

void USART_Transmit(uint8_t data )
{
	
	/* Wait for empty transmit buffer */
	while ( !( UCSR0A & (1<< UDRE0 )) )
	;
	/* Put data into buffer, sends the data */
	UDR0 = data;
	
}

unsigned char USART_Receive( void )
{
	/* Wait for data to be received */
	while ( !(UCSR0A & (1<<RXC0)) )
	;
	/* Get and return received data from buffer */
	return UDR0;
}

void USART_SendString(char *str)
{
	unsigned char j=0;
	
	while (str[j]!=0)		/* Send string till null */
	{
		USART_Transmit(str[j]);
		j++;
	}
}
Posted on Leave a comment

How to ATMega328PB to avrdude.conf

I recently bought a few Arduino Uno clones. They were not cheap. They utilise the ch34 IC, which is a USB to UART solution. They all have SMD atmega328pb soldered on them.

The problem is that two of them stopped working with the Arduino ide. On inspection, i found out that my software was not updated so firstly I updated the software which now includes a configuration for the atmega328pb.

But somehow the internal bootloader in these SMD chips got corrupted and it stopped working. I then probed the chip with AVRdude prograamer.

Found that the chip is atmega328pb. The configurations were not included at the time of installation. But the avrdude program allows you added other avr parts from Atmel.

For Windows 10

  1. Go to
C:\WinAVR-20100110\bin

2. Then open

Posted on Leave a comment

LM317 Adjustable Voltage Regulator

This is a very inexpensive voltage regulator IC. Which can be either used as a fixed voltage regulator or variable voltage regulator.

It just needs only two resistors for setting the output voltage.

LM317 is made by different companies. ST, Ti, On Semi, CDIL, HTC Korea etc.

For setting the output voltage

V(output) = [ V(reference) * ( 1 + R2/R1) ] + [ I(adjustment) * R2 ]

Vref = 1.25V

I(adj) = 50 uA

For setting 5 V at the output

R2 = 2.2k + 680 ohm

R1 = 1k ohm

There are many version of this chip

Lm317M for 500mA maximum current

LM317T for 1.5A max current

LM317L for 100mA max current

Posted on Leave a comment

Battery Monitoring with Led Light Control Using ESP32 Bluetooth for my Solar System

I need to monitor battery voltage to check weather my charging system is working correctly or not. But to do that i have get up and walk with my multimeter towards the battery and i have to take these reading in night.

I placed my battery in a corner where there is very little light. So I added a transistor switch which can be controlled using Bluetooth to turn on the led light. Which provides enough light to act as a night light.

The ESP32 also has an ADC built into it. Which is very poor in terms of accuracy. It gets you the idea that there is something to work with but it does not give very precise reading like a multimeter.

Also, the ESP32 ADC is non-linear. The ADC also has an attenuation feature. Which by default is set to 11db. which gives us a workable range of up to 3.3V. But there are flat-out regions which need to be taken into account if you want to measure anything from this ADC. There is a 0 – 0.2V region in which the value read is constant, and there is a 2.9V to 3.3V region which also gives you constant reading values.

Resolution is 12-bit by default.

To measure a large voltage using this device. I made a voltage divider.

Battery +ve———/\/\/ R1 \/\/\/ ———— Vout —————— /\/\/\/\/ R2 \/\/\/\/ ———-GND

R1 = 10 kilo Ohm

R2 = 1 kilo Ohm

Which gives me a dividing factor of 11.

So if 11 V is available at the battery anode. Then the V out is 1V.

Normally the lead acid battery voltage goes from 10V(fully discharged) to 14.6V(Maximum charge) to 15V(Over Charged)

The ADC values are converted to battery voltage using the following equation

( (analogValue * (3.3/4096 ) ) * ((9820+985)/985) ) + 3.3

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;


char var1;
int analogValue;
uint8_t i;
int avg_adc;

void setup()
{
  Serial.begin(115200);
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("pair it with bluetooth!");
pinMode(2, OUTPUT);
 
}

void loop()
{
  /*
   * UART 
   */
  if (Serial.available()) {
  
    SerialBT.write(Serial.read());
  }


/*
 * Bluetooth serial
 */
  if (SerialBT.available()) {
      var1 = SerialBT.read();
    Serial.write(var1);
  }
    if (var1 == '9')
{
  digitalWrite(2,HIGH);
}
else if(var1 == '1')
{
  digitalWrite(2,LOW);
}


avg_adc = 0;
for(i =0 ; i< 100; i++)
{
  avg_adc += analogRead(34);
}
analogValue = avg_adc / 100;

SerialBT.printf("ADC = %d\n",analogValue);
  SerialBT.printf("volt = %f\n",   ( (analogValue * (3.3/4096 ) ) * ((9820+985)/985) ) + 3.3 ) ;
    delay(1000);
}

Since the ADC is not accurate it goes all over the place. To dampen its effect on the reading I am averaging 100 readings of ADC.

avg_adc = 0;
for(i =0 ; i< 100; i++)
{
  avg_adc += analogRead(34);
}
analogValue = avg_adc / 100;

TIP3055 BJT is used as a low-side switch. R1 gives a base current of 330uA(=3.3/10000) which gets multiplied by the beta or hFE 70 of transistor to get a collector current of 0.023A or 23mA.