Posted on Leave a comment

How to use S109AFTG Microstep Driver with ATmega328PB Programmed using Microchip Studio

When I opened the case. I found a PCB which is screwed to a big heatsink. I unscrewed the bolts and saw that there is S109AFTG. The IC is sandwiched between the PCB and the heatsink. A small aluminum block is also used for heat transfer between the IC and the heatsink.

It has a different step size which can be selected by the DIP switches.
The motor driver has a maximum of 1/32 step size.

which means 1.8°/ 32 = 0.05625°

360°/ 0.05625° = 6400 steps

So a full rotation will be in 6400 steps.

You will need a power source such as a Switched Mode Power Supply which can supply at least 2 Amps.

If your application needs more torque you will need a power source that can provide a high current without dropping the voltage.

Or you can use the battery for a short duration.

Schematic Diagram

Code

/*
* main.c
*
* Created: 7/4/2023 5:51:21 PM
*  Author: abhay
*/
#define F_CPU 16000000
#include <xc.h>
#include <util/delay.h>
int PUL=PIND6; //define Pulse pin
int DIR=PINB1; //define Direction pin
int ENA=PIND2; //define Enable Pin
#define DirLow PORTB &= ~(1<<DIR)
#define DirHigh PORTB |= (1<<DIR)
#define PulLow PORTD &= ~(1<<PUL)
#define PulHigh PORTD |= (1<<PUL)
#define EnaLow PORTD &= ~(1<<ENA)
#define EnaHigh PORTD |= (1<<ENA)
#define delayus50 _delay_us(50)
int main(void)
{
	DDRB |= (1<<DIR);
	DDRD |= (1<<PUL)|(1<<ENA);
	while(1)
	{
		//TODO:: Please write your application code
		for (int i=0; i<6400; i++)    //Forward 6400 steps
		{
			DirLow;
			EnaHigh;
			PulHigh;
			delayus50;
			PulLow;
			delayus50;
		}
		_delay_ms(5000);
		for (int i=0; i<6400; i++)   //Backward 6400 steps
		{
			DirHigh;
			EnaHigh;
			PulHigh;
			delayus50;
			PulLow;
			delayus50;
		}
		_delay_ms(2000);
	}
}
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

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.