/*
 * motor.c
 *
 * Library of useful motor commands to interface to the Motor Mind B module.
 *
 *  Copyright (C) 1998  Mark Crosbie mark@mastincrosbie.com
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * The Motor Mind B is available from Solutions Cubed Electronics
 * www.solutions-cubed.com
 *
 * It communicates at 2400 baud. It must be sent a SYNC byte (0x55)
 * before every message so it can sync up on the baud rate.
 *
 * Not tied to any port or pin. Specify the port,pin for each routine
 *
 * Copyright Mark Crosbie  9/20/98
 *
 */

/******************* REDEFINE AS NECESSARY ********************/

#define MMSYNC 0x55
#define MMSTOP 0
#define MMREV 1
#define MMTACH 2
#define MMSETDC 3
#define MMSPDCON 4
#define MMSTATUS 5
#define MMCOUNT 6

/* defined in delays.c */
extern void delay_ms(char);

/* how to turn on a bit in a byte */
/*
  const char bitson[] = {1,2,4,8,16,32,64,128};
  const char bitsoff[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
*/

/* Send a byte to the Motor Mind B
 *
 *** Assumes 2400 8N1 communication
 *** The constant MMPIN defines which pin the Module is on
 *** The constant MMPORT defines which port the Module in on
 *
 * Arguments:  port: port for motor: either PORTA or PORTB
 *             pin: pin driving FM line on module. 0 <= pin <= 7
 *             c: byte to send to motor in range 0 <= c <= 255
 *
 * NON-INVERTED!
 *
 * !!! Do not assume that the parameter values are unchanged after
 *     calling. !!!
 *
 * Return Value: none
 */
void mm_putc(char port, char pin, char c) {
  char i, biton, bitoff, bitcount;

  /* This code adapted from:
   * Copyright, Peter H. Anderson, Morgan State University, June 14, '97
   */

  /* compute the correct left shifted value based on pin
   * The pin value is in the range (0,7) on input, but we want it in
   * the range (1,8) for this loop to work.
   * Destroys parameter pin, but it's not needed again
   * bitoff is just the complement of biton (invert bits)
   */
asm incf param01_mm_putc,f
asm clrf _biton_mm_putc
asm bsf STATUS, C
asm rlf _biton_mm_putc, f
asm decfsz param01_mm_putc, f
asm goto $-2
asm comf _biton_mm_putc, w
asm movwf _bitoff_mm_putc

  /* set the INDF register to point to the port register */
asm movf param00_mm_putc, w
asm movwf FSR

  /* turn on the bit  - set the STOP bit */
asm        movf INDF, w
asm        iorwf _biton_mm_putc, w
asm        movwf INDF

  i = 255;			    
  while(i-- != 0);  /* let stop bit be set for a while */

asm        MOVLW   9
asm        MOVWF   _bitcount_mm_putc
asm        BCF     STATUS, C       ; set C to 0, start bit

/* if carry is set then we set the output high. 
 * So we test if C is set, and if so, we skip the jump around the code
 */
asm        BTFSS   STATUS, C
asm        goto $+6
asm        movf INDF, w
asm        iorwf _biton_mm_putc, w
asm        movwf INDF

asm        BTFSC   STATUS, C
asm        goto $+4
asm        movf INDF,w
asm        andwf _bitoff_mm_putc, w
asm        movwf INDF

      /* this takes some computation. Can range over 130-132 */
asm        MOVLW   134
asm        MOVWF   _i_mm_putc       ; one bit delay.  416 usecs at 2400 baud

asm        DECFSZ  _i_mm_putc, F
asm        GOTO $-1
asm        NOP

asm        RRF     param02_mm_putc, F    ; least sign bit now in C
asm        DECFSZ  _bitcount_mm_putc, F    ; does not affect status
asm        GOTO    $-17        ; next character

  /* set the stop bit */
asm        movf INDF, w
asm        iorwf _biton_mm_putc, w
asm        movwf INDF

  delay_ms(1);  /* a pacing delay of 1ms between characters */
}

/* stop the specified motor
 * Arguments:
 *   port: motor port (either 0x5 or 0x6 for PORTA or PORTB)
 *   pin: pin on the port for the motor
 * Return value:
 *   none
 */
void mm_stop(char port, char pin) {
  mm_putc(port, pin, MMSYNC);
  mm_putc(port, pin, MMSTOP);
}

/* start the specified motor
 * Send the SETDC command followed by the dutycycle
 * Arguments:
 *   port: motor port (either 0x5 or 0x6 for PORTA or PORTB)
 *   pin: pin on the port for the motor
 *   dutycycle: speed of motor from 0 (min) to 255 (max)
 * Return value:
 *   none
 */
void mm_start(char port, char pin, char dutycycle) {
  mm_putc(port, pin, MMSYNC);
  mm_putc(port, pin, MMSETDC);
  mm_putc(port, pin, dutycycle);
}

/* reverse the specified motor 
 * Arguments:
 *   port: motor port (either 0x5 or 0x6 for PORTA or PORTB)
 *   pin: pin on the port for the motor
 * Return value:
 *   none
 */
void mm_reverse(char port, char pin) {
  mm_putc(port, pin, MMSYNC);
  mm_putc(port, pin, MMREV);
}
