Skip to content

Instantly share code, notes, and snippets.

@bigjosh
Last active May 13, 2018 21:19
Show Gist options
  • Save bigjosh/cafe41aaec7046380f16a8e53e97d57d to your computer and use it in GitHub Desktop.
Save bigjosh/cafe41aaec7046380f16a8e53e97d57d to your computer and use it in GitHub Desktop.
PWM Motor control code for AIRBOAT running on ATTINY10
/*
* Attiny10 AIRBOAT PWM Motor control code
*
* Created: 12/2/2016 6:17:47 PM
* Author : josh
*/
#include <avr/io.h>
#include <util/delay.h>
// MOTOR FUNCTIONS
// ===============
// Note that register values are hard coded rather than #defined because they
// can not just be moved around. They depend on the Timer hardware of the specific chip/pin.
// Motor is controller by an N-MOSFET which is connected to pin OC0B (pin #3 on ATTINY10 SOT-23).
// 1 output turns motor on, 0 is off.
// Initialize the motor pin. Sets to output mode, which will drive pin LOW.
// Call this as soon as possible after reset to keep the mosfet from floating and turning on the motor.
static void initMotor() {
DDRB |= _BV(1); // Set pin to output mode. It will already be low because IO ports default to 0 on reset.
}
// Turn the motor completely off- disconnects from PWM generator
static void motorOff(void) {
TCCR0A &= ~( _BV(COM0B0) | _BV(COM0B1) ); // Set COM0B0 and COM0B1 to 0, "Normal port operation, OC0B disconnected"Z
// Will revert the pin output back to PORT value, which we always keep at the default (zero).
}
// Set motor PWM
// match sets the duty cycle and should be between 0 and top. 0=completely off, top=full on.
// top sets the frequency where PWM frequency = F_CPU/top. The minimum resolution allowed is 2-bit (top set to 0x0003).
// Note: Currently disables OCR0A, but could easily be changed to preserve OCR0A through speed changes if wanted.
// Note: Clobbers ICR0 for TOP function.
// Note: Resets all used registers each time from scratch for safety from glitches. This is probably unnecessary...
// http://electronics.stackexchange.com/questions/139575/how-often-do-avrs-actually-glitch-and-need-a-watch-dog-reset-in-the-real-world/144318
static void setMotorPWM( uint16_t match , uint16_t top ) {
if (match==0) { // Special case this because the PWM generator still generates a pulse at 0 duty cycle
// "If the OCR1x is set equal to BOTTOM (0x0000) the output will be a narrow spike for each TOP+1 timer clock cycle."
motorOff();
} else {
TCCR0A =
_BV(COM0B1) | // Clear OC0B on compare match (non-inverting)
_BV(WGM01) // Mode 14 1110, Fast PWM TOP=ICR0
; // Note that this clears the OC0A COM bits as side effect, but those could be preserved if wanted
TCCR0B =
_BV( WGM03) | _BV( WGM02 ) | // Mode 14 1110, Fast PWM TOP=ICR0
_BV( CS00 ) // prescaler=clk/1
;
OCR0B = match;
ICR0 = top; // Counter top value - resets to zero when we get here
if (TCNT0 > top ) { // Handle the glitch in the case where changing TOP leads a missed match and a full wrap of TCNT
TCNT0 = top-1;
}
// Note that there is a silicon bug that might make this not work on older parts:
// http://electronics.stackexchange.com/questions/97596/attiny85-pwm-why-does-com1a0-need-to-be-set-before-pwm-b-will-work
}
}
int main(void)
{
DDRB |= _BV( 2 ); // Set pin PB2 to output so we can blink an attached LED for feedback
initMotor();
while (1)
{
PINB |= _BV( 2 ); // Blink LED once per cycle so we know we are running!
_delay_ms(500);
setMotorPWM( 10 , 100 );
_delay_ms(1000);
setMotorPWM( 100 , 1000 );
_delay_ms(1000);
setMotorPWM( 1000 , 10000 );
_delay_ms(1000);
motorOff();
_delay_ms(1000);
setMotorPWM( 20 , 100 );
_delay_ms(1000);
setMotorPWM( 50 , 100 );
_delay_ms(1000);
setMotorPWM( 80 , 100 );
_delay_ms(1000);
motorOff();
_delay_ms(1000);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment