Friday, October 19, 2012

RS232 Stepper motor control attiny2313 C source code


This code is from project: OpenWrt antenna rotator , Android tablet controlling stepper motor

delay.h

#ifndef _H_DELAY_
#define _H_DELAY_

#define nop()  __asm__ __volatile__("nop")

void delay_ms(unsigned short ms);
void delay_us(unsigned short us);

#endif

delay.c

#include "main.h"
#include <util/delay.h>

#include "delay.h"

/****************************************************************************/
/* It is better to put a delay in the loop than provide high value to       */
/* standard function, because the code will be smaller,                     */
/****************************************************************************/
void delay_ms(unsigned short ms) {
    while ( ms ) {
      ms--;
      _delay_ms(1);
   }
}

void delay_us(unsigned short us) {
    while ( us ) {
      us--;
      _delay_us(1);
   }
}

main.h

#ifndef _H_MAIN_H
#define _H_MAIN_H

// define CPU speed, in our case is 1MHz from internal oscilator (default)
#define F_CPU 1000000L

// some usefeuul macros
#define H(a,b) a |=(1<<(b))
#define L(a,b) a &=~(1<<(b))
#define IS(a,b) bit_is_set(a,b)
#define BS(a,b) (a & (1<<(b)))

#endif

main.c

#include "main.h"

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>

#include "uart.h"
#include "motor.h"
#include "delay.h"



/****************************************************************************/
/* This is the interrupt generated from RS232.                              */
/* This funtion will be run always after some data can be recived from      */
/* attiny's USART port.                                                     */
/****************************************************************************/
SIGNAL(SIG_USART0_RECV) {
   uint8_t chr;

   static uint8_t  nDataCnt =  0;
   static uint8_t  nData    =  0;

   // data counter
   nDataCnt++;

   // get one character from USART (RS232)
   chr = RxByte();

   /*
    * If it is a 'R' it means that we want to begin propper data
    * transmission.
    * 'R" aslo resets already recived data
    */
   if (chr == 'R') {
      nData = 0;
      nDataCnt = 0;

      // send reset confirm
      TxString("R");
      return;
   }
 
   /* Switch is used to merge recieved data from port.
    * Message is 3 characters long, it's begin with a
    * two digit number and followed by one char that was
    * added as a future. Maybe someday it would be 
    * driven also in vertical way by another motor.
    */
   switch(nDataCnt)
   {
      
      case 1:
         // getting first byte -> it must be a number
         nData = nData + ((chr -48) * 10);
      break;

      case 2:
         // getting second byte -> it must be a number
         nData = nData + ((chr -48) * 1);
      break;

      case 3:
         // getting lasat byte -> it must be char 'H'
         switch(chr)
         {
            case 'H':
               if(nData >= 0 && nData <= 90) {

                  // nData after merge is a number from 0 to 90

                  // we can now set motor position

                  setPos(nData);

                  // after change motor position, we can send
                  // confirmation of propper behaviour.
                  TxString("O");

                  nData = 0;
                  nDataCnt = 0;
                  return;
               }
               else
                  // something went wrong, sending 'E' as a ERROR
                  TxString("E");

            default:
               // like above 'E' -> ERROR
               nData = 0;
               nDataCnt = 0;
               TxString("E");
               return;
         };         
   };
}  


/****************************************************************************/
/* This is the main loop, it's not comlicated, because we work on           */
/* interrupts.                                                              */
/****************************************************************************/
int main()
{

   // init serial communication, interrupts etc.
   InitUart();
  

   // init output port for motor control
   DDRB = 0x0F;
   PORTB = 0x00;

   // set zero position (we already at 0 after restart, so 'do nothing'
   setPos(0); 

   // enable global interrupts
   sei();

   // infinite loop
   while(1) { 
   }
}

motor.h

#ifndef _H_MOTOR_H
#define _H_MOTOR_H

void left();
void right();
void setPos(unsigned short);
void step();



#endif

motor.c

#include "main.h"

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>

#include "motor.h"
#include "delay.h"


// global variables for stepper motor position
static signed short  stepPos;
static signed short  pos;


/****************************************************************************/
/* This function is used to make one step left.                             */
/****************************************************************************/
void left() {
   stepPos++;
   if (stepPos > 3)
      stepPos = 0;
   step();
}


/****************************************************************************/
/* This function is used to make one step right.                            */
/****************************************************************************/
void right() {
   stepPos--;

   if (stepPos < 0)
      stepPos = 3;

   step();
}


/****************************************************************************/
/* setPos function is calculating how many time we have to run 'left()' or  */
/* 'right()' to get propper position.                                       */
/****************************************************************************/
void setPos(unsigned short newPos) {

   unsigned short ammount;

   // 'pos' is current position
   
   if (newPos > pos) {

      // calculating distance
      ammount = newPos - pos;

      while(ammount--) 
         right();

      // stop
      PORTB = 0;
   }

   if (newPos < pos) {
      ammount = pos - newPos;
      while(ammount--)
         left();

      // stop
      PORTB = 0;
   }

   PORTB = 0;

   // remember new position as actual position (we already there)
   pos = newPos;
}


/****************************************************************************/
/* Stepper motor has 4 positions. By 'step' function we send accurate       */
/* value to port and motor is changing position to required.                */
/****************************************************************************/
void step() {
   switch(stepPos)   {
      case 0:
         // one
         PORTB = 5;
      break;

      case 1:
          // two
         PORTB = 6;
      break;

      case 2:
         // three
         PORTB = 10;
      break;

      case 3:
         // four
         PORTB = 9;
      break;
   };

   /* Delay needs to be done here, because changing position
    * last some time and motor need time to react.
    */
   delay_ms(50);
}

uart.h

#ifndef _H_UART_H
#define _H_UART_H


//#define USART_BAUDRATE 9600
//#define BAUD_PRESCALE (((F_CPU  / (USART_BAUDRATE * 16UL ) )) -1 )

unsigned char RxByte(void);
void TxByte(unsigned char );
void TxString(const char *);
void InitUart(void);

#endif

uart.c

#include "main.h"

#include <avr/io.h>

#include "uart.h"


/****************************************************************************/
/* At the beginnig, we should init the uasrt port on our MCU.               */
/****************************************************************************/
void InitUart (void) {


   // 1mhz 9600 U2X
   UBRRH = 0;
   UBRRL = 12;

   // x 2
   UCSRA = (1<<U2X);

   // enable receiving, sending and receive  innterrupt.
   UCSRB = (1<<RXEN) | (1<<TXEN) | (0<<TXCIE) | (1<<RXCIE);

   // 8N1
   UCSRC = (1 << UCSZ1) | (1 << UCSZ0);

}


unsigned char RxByte(void) {

   // wait to complete receive
   while (!(UCSRA & (1 << RXC)));

   // return register in which is recieved byte
   return UDR;
}


void TxByte(unsigned char data) {

   // wait for green light for transmit
   while (!(UCSRA & (1 << UDRE)));
   
   // send byte to port
   UDR = data;
}


void TxString (const  char *str) {  
   int n = '\0';
    
   // send whole string to port
   while ( str[n] !=  '\0') {
      while (!(UCSRA & (1 << UDRE)));
      
      UDR = str[n++];
   }
}

Makefile

CPU=attiny2313

GCC=avr-gcc
CFLAGS= -Os -mmcu=$(CPU) -Wall -fpack-struct -fshort-enums -funsigned-bitfields -Wl,--relax -fno-move-loop-invariants -funsigned-char -fno-inline-small-functions -fdata-sections -fno-tree-loop-optimize -lprintf_min 

INCLUDES=
LIBS=

OBJCPY=avr-objcopy
OBJECTS=main.o uart.o delay.o motor.o

PROJECT_NAME=antenaDriver
HEX_FILE=$(PROJECT_NAME).hex
HEX_FILE_DUMP=$(PROJECT_NAME)_dump.hex

# this line shoud be replaced by your programmator
# I use simplest ISP by parallel port and bunch of 
# wires and resistors.
PROG=uisp
PROG_FLAGS=-dprog=dapa -dpart=attiny26

all: cls $(PROJECT_NAME) obj size #upload

$(PROJECT_NAME): $(OBJECTS)
  $(GCC) -o $(PROJECT_NAME) $(OBJECTS) $(CFLAGS) $(LIBS) $(INCLUDES)

main.o: main.c main.h 
  $(GCC) $(CFLAGS) $(INCLUDES) -c main.c

uart.o: uart.c uart.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c uart.c

delay.o: delay.c delay.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c delay.c

motor.o: motor.c motor.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c motor.c

obj: $(OBJECTS)
  $(OBJCPY) -O ihex $(PROJECT_NAME) $(HEX_FILE)

clean:
  rm -f $(PROJECT_NAME) $(OBJECTS) $(HEX_FILE)

cls:
  clear

size:
  du -b $(HEX_FILE) 

upload: all $(HEX_FILE)
  $(PROG) $(PROG_FLAGS) --erase --upload if=$(HEX_FILE)

download:
  $(PROG) $(PROG_FLAGS) --download of=$(HEX_FILE_DUMP)

1 comment: