ATTiny85 PWM Motor speed control newbie

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assembly.
The attempt is to produce a phase correct PWM signal on PB0 that will drive a MOSFET that will drive a motor using the ADC input and a pot to control the speed.

Here is what I have so far. All of the control bit setting is causing me confusion.

;
;========================================================
;  SETUP THE TIMER COUNTER FOR PHASE CORRECT PWM MODE
;========================================================
;
;
	ldi temp, 0b11111111
	out OCR0A, temp			;set TOP
	ldi temp, 0b10000001
	out TCCR0A, temp
	ldi temp, 0b00001001
	out TCCR0B, temp
	rcall DELAY
;

I assume the DELAY function would use the ADC port pin and the pot to set the time delay (speed of the motor) or do I have to load a value into OCR0A to do this?

Is there anything I need to add to the above code to get the PWM output part of the project working?

I have read (over and over) the "Using AVR Counter" document and the "AT Tiny Data Sheet" but just can't seem to multitask between all of the bit settings and acronyms used to create a coherent mental picture of how all of this goes together.

Please help. 

BADBAUD

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I assume the DELAY function would use the ADC port pin and the pot to set the time delay
Why would you need a delay function when you are using PWM?

If you are setting TOP as 0xff, why not use mode 1 instead of mode 5? And if you are using mode 5, then you can't use OCR0A as output. And which ever output you are using, you need to set the OCR0x register to change the duty cycle.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I set the TOP as arbitrary 0xFF to test the code if I can ever figure it out.

Eventually I want to plug in a number, any number from 0x01 to 0xFF.

What output could I use if if I want mode 5?

The DELAY function would adjust the speed of the motor?, I thought I could plug the 8 bit ADC result into the OCR0A register to change the duty cycle.

Is this logic incorrect also?

BADBAUD

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is not tested as I do not have an ATtiny85.

No guarantees or warranties implied.

/* 
***************************************************************************
**
**  Counter/Timer Examples -- Code Examples for Tutorial
**
**  3/6/09 - Initial Creation -- jkl
**  6/10/2012 - striped for AVRFreak badbaud's benefit
**
**************************************************************************
*/

#include 

// Logic dictates
#define	TRUE	1
#define	FALSE	0

int main(void)
{

	// Configure ports
	DDRB |= (1 << PB4);	// Enable CT0 output pin OC0B
	PORTB &= ~(1 << PB4);	// Set OC0B to low - LED off

	// CT0 in Phase Correct PWM Mode.
	// Generating a Waveform:
	// OCR0A as TOP, OCR0B as compare, OC0B as output.
	// Vary OCR0A to change cycle time.

	uint8_t count = 0;

	// Set OCR0A (TOP) and OCR0B
	OCR0A = 255;
	OCR0B = 49;

	// Configure Timer/Counter 0 
	// Select Phase Correct PWM Mode, and OCR0A as TOP. Use WGM Bits.
	// WGM Bits are in TCCR0A and TCCR0B. Any previous settings are cleared.
	TCCR0A = (1 << WGM00);
	TCCR0B = (1 << WGM02);

	// OC0B LO when COUNT = OCR0B upcounting, HI when COUNT = OCR0B downcounting.
	TCCR0A |= (1 << COM0B1);

	// Select Internal Clock Divided by 1024. This starts the Timer/Counter.
	TCCR0B |= (1 << CS02) | (1 << CS00);

	while (TRUE)		// Do this forever
	{
		// monitor the overflow flag and change the time every 10 cycles
		while ((TIFR & (1 << TOV0)) == 0) ;	// wait for timer overflow
		TIFR |= (1 << TOV0);	// Clear the flag by writing 1

		if (count >= 10) {
			if (OCR0A > 100) {
				OCR0A = 98;	// cycle length two tenths second
			} else {
				OCR0A = 255;	// cycle length two seconds
			}
			count = 0;	// reset count
		} else {
			count++;
		}
	}
}
CT_examples.elf:     file format elf32-avr

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000000a8  00000000  00000000  00000054  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .stab         000006cc  00000000  00000000  000000fc  2**2
                  CONTENTS, READONLY, DEBUGGING
  2 .stabstr      00000085  00000000  00000000  000007c8  2**0
                  CONTENTS, READONLY, DEBUGGING
  3 .debug_aranges 00000080  00000000  00000000  00000850  2**3
                  CONTENTS, READONLY, DEBUGGING
  4 .debug_info   0000032c  00000000  00000000  000008d0  2**0
                  CONTENTS, READONLY, DEBUGGING
  5 .debug_abbrev 00000099  00000000  00000000  00000bfc  2**0
                  CONTENTS, READONLY, DEBUGGING
  6 .debug_line   0000029e  00000000  00000000  00000c95  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_frame  00000024  00000000  00000000  00000f34  2**2
                  CONTENTS, READONLY, DEBUGGING
  8 .debug_str    00000086  00000000  00000000  00000f58  2**0
                  CONTENTS, READONLY, DEBUGGING
  9 .debug_loc    0000002a  00000000  00000000  00000fde  2**0
                  CONTENTS, READONLY, DEBUGGING

Disassembly of section .text:

00000000 <__vectors>:
   0:	0e c0       	rjmp	.+28     	; 0x1e <__ctors_end>
   2:	28 c0       	rjmp	.+80     	; 0x54 <__bad_interrupt>
   4:	27 c0       	rjmp	.+78     	; 0x54 <__bad_interrupt>
   6:	26 c0       	rjmp	.+76     	; 0x54 <__bad_interrupt>
   8:	25 c0       	rjmp	.+74     	; 0x54 <__bad_interrupt>
   a:	24 c0       	rjmp	.+72     	; 0x54 <__bad_interrupt>
   c:	23 c0       	rjmp	.+70     	; 0x54 <__bad_interrupt>
   e:	22 c0       	rjmp	.+68     	; 0x54 <__bad_interrupt>
  10:	21 c0       	rjmp	.+66     	; 0x54 <__bad_interrupt>
  12:	20 c0       	rjmp	.+64     	; 0x54 <__bad_interrupt>
  14:	1f c0       	rjmp	.+62     	; 0x54 <__bad_interrupt>
  16:	1e c0       	rjmp	.+60     	; 0x54 <__bad_interrupt>
  18:	1d c0       	rjmp	.+58     	; 0x54 <__bad_interrupt>
  1a:	1c c0       	rjmp	.+56     	; 0x54 <__bad_interrupt>
  1c:	1b c0       	rjmp	.+54     	; 0x54 <__bad_interrupt>

0000001e <__ctors_end>:
  1e:	11 24       	eor	r1, r1
  20:	1f be       	out	0x3f, r1	; 63
  22:	cf e5       	ldi	r28, 0x5F	; 95
  24:	d2 e0       	ldi	r29, 0x02	; 2
  26:	de bf       	out	0x3e, r29	; 62
  28:	cd bf       	out	0x3d, r28	; 61

0000002a <__do_copy_data>:
  2a:	10 e0       	ldi	r17, 0x00	; 0
  2c:	a0 e6       	ldi	r26, 0x60	; 96
  2e:	b0 e0       	ldi	r27, 0x00	; 0
  30:	e8 ea       	ldi	r30, 0xA8	; 168
  32:	f0 e0       	ldi	r31, 0x00	; 0
  34:	02 c0       	rjmp	.+4      	; 0x3a <__CCP__+0x6>
  36:	05 90       	lpm	r0, Z+
  38:	0d 92       	st	X+, r0
  3a:	a0 36       	cpi	r26, 0x60	; 96
  3c:	b1 07       	cpc	r27, r17
  3e:	d9 f7       	brne	.-10     	; 0x36 <__CCP__+0x2>

00000040 <__do_clear_bss>:
  40:	10 e0       	ldi	r17, 0x00	; 0
  42:	a0 e6       	ldi	r26, 0x60	; 96
  44:	b0 e0       	ldi	r27, 0x00	; 0
  46:	01 c0       	rjmp	.+2      	; 0x4a <.do_clear_bss_start>

00000048 <.do_clear_bss_loop>:
  48:	1d 92       	st	X+, r1

0000004a <.do_clear_bss_start>:
  4a:	a0 36       	cpi	r26, 0x60	; 96
  4c:	b1 07       	cpc	r27, r17
  4e:	e1 f7       	brne	.-8      	; 0x48 <.do_clear_bss_loop>
  50:	02 d0       	rcall	.+4      	; 0x56 
52: 28 c0 rjmp .+80 ; 0xa4 <_exit> 00000054 <__bad_interrupt>: 54: d5 cf rjmp .-86 ; 0x0 <__vectors> 00000056
: int main(void) { // Configure ports DDRB |= (1 << PB4); // Enable CT0 output pin OC0B 56: bc 9a sbi 0x17, 4 ; 23 PORTB &= ~(1 << PB4); // Set OC0B to low - LED off 58: c4 98 cbi 0x18, 4 ; 24 // Vary OCR0A to change cycle time. uint8_t count = 0; // Set OCR0A (TOP) and OCR0B OCR0A = 255; 5a: 8f ef ldi r24, 0xFF ; 255 5c: 89 bd out 0x29, r24 ; 41 OCR0B = 49; 5e: 81 e3 ldi r24, 0x31 ; 49 60: 88 bd out 0x28, r24 ; 40 // Configure Timer/Counter 0 // Select Phase Correct PWM Mode, and OCR0A as TOP. Use WGM Bits. // WGM Bits are in TCCR0A and TCCR0B. Any previous settings are cleared. TCCR0A = (1 << WGM00); 62: 81 e0 ldi r24, 0x01 ; 1 64: 8a bd out 0x2a, r24 ; 42 TCCR0B = (1 << WGM02); 66: 88 e0 ldi r24, 0x08 ; 8 68: 83 bf out 0x33, r24 ; 51 // OC0B LO when COUNT = OCR0B upcounting, HI when COUNT = OCR0B downcounting. TCCR0A |= (1 << COM0B1); 6a: 8a b5 in r24, 0x2a ; 42 6c: 80 62 ori r24, 0x20 ; 32 6e: 8a bd out 0x2a, r24 ; 42 // Select Internal Clock Divided by 1024. This starts the Timer/Counter. TCCR0B |= (1 << CS02) | (1 << CS00); 70: 83 b7 in r24, 0x33 ; 51 72: 85 60 ori r24, 0x05 ; 5 74: 83 bf out 0x33, r24 ; 51 // CT0 in Phase Correct PWM Mode. // Generating a Waveform: // OCR0A as TOP, OCR0B as compare, OC0B as output. // Vary OCR0A to change cycle time. uint8_t count = 0; 76: 80 e0 ldi r24, 0x00 ; 0 if (count >= 10) { if (OCR0A > 100) { OCR0A = 98; // cycle length two tenths second } else { OCR0A = 255; // cycle length two seconds 78: 4f ef ldi r20, 0xFF ; 255 } count = 0; // reset count 7a: 20 e0 ldi r18, 0x00 ; 0 while ((TIFR & (1 << TOV0)) == 0) ; // wait for timer overflow TIFR |= (1 << TOV0); // Clear the flag by writing 1 if (count >= 10) { if (OCR0A > 100) { OCR0A = 98; // cycle length two tenths second 7c: 32 e6 ldi r19, 0x62 ; 98 TCCR0B |= (1 << CS02) | (1 << CS00); while (TRUE) // Do this forever { // monitor the overflow flag and change the time every 10 cycles while ((TIFR & (1 << TOV0)) == 0) ; // wait for timer overflow 7e: 08 b6 in r0, 0x38 ; 56 80: 01 fe sbrs r0, 1 82: fd cf rjmp .-6 ; 0x7e TIFR |= (1 << TOV0); // Clear the flag by writing 1 84: 98 b7 in r25, 0x38 ; 56 86: 92 60 ori r25, 0x02 ; 2 88: 98 bf out 0x38, r25 ; 56 if (count >= 10) { 8a: 8a 30 cpi r24, 0x0A ; 10 8c: 48 f0 brcs .+18 ; 0xa0 if (OCR0A > 100) { 8e: 89 b5 in r24, 0x29 ; 41 90: 85 36 cpi r24, 0x65 ; 101 92: 18 f0 brcs .+6 ; 0x9a OCR0A = 98; // cycle length two tenths second 94: 39 bd out 0x29, r19 ; 41 } else { OCR0A = 255; // cycle length two seconds } count = 0; // reset count 96: 82 2f mov r24, r18 98: f2 cf rjmp .-28 ; 0x7e if (count >= 10) { if (OCR0A > 100) { OCR0A = 98; // cycle length two tenths second } else { OCR0A = 255; // cycle length two seconds 9a: 49 bd out 0x29, r20 ; 41 } count = 0; // reset count 9c: 82 2f mov r24, r18 9e: ef cf rjmp .-34 ; 0x7e } else { count++; a0: 8f 5f subi r24, 0xFF ; 255 a2: ed cf rjmp .-38 ; 0x7e 000000a4 <_exit>: a4: f8 94 cli 000000a6 <__stop_program>: a6: ff cf rjmp .-2 ; 0xa6 <__stop_program>

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Tue. Jun 12, 2012 - 03:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Larry!
Thank you very much. Sorry I did not respond sooner. I finally have some time tonight to go over the code and will let you know how it turns out.

BTW I am using a ATTiny85 - not a mega. Thought I would start small and work my way up.

BADBAUD

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

badbaud wrote:
BTW I am using a ATTiny85 - not a mega. Thought I would start small and work my way up.

Thanks for pointing out the typo, I edited my post. I built the code for the ATtiny85. Not much to work with on that chip. A challenge is always fun though.

Good luck.

EDIT: By the way, the comments for the timing are all wrong. The original code was written for a 16 bit timer and this one is 8 bit.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Larry, after 4 days of internet hell I finally had to log on with a new username and password.

Something about cookies having to be enabled. Or am I blocked for asking stupid questions??

Anyways, thank you for the sample code.

It helped me get this project done.
Looking on pin 6 of the ATTiny I get from a 2 ms pulse every 2 ms to a 2 ms pulse every 500 ms by varying the TOP value.

here is my code, if I am truly blocked from participating from this forum I would sure like to know why.

; ============================================
;        R E S E T    V E C T O R
; ============================================
;
.CSEG
.ORG $0000
	rjmp Main ; Reset vector
;
; ============================================
;     M A I N    P R O G R A M    I N I T
; ============================================
;
Main:
; Init stack
	ldi TEMP, HIGH(RAMEND) ; Init MSB stack
	out SPH,TEMP
	ldi TEMP, LOW(RAMEND) ; Init LSB stack
	out SPL,TEMP
;
;
; Init Port B  0=INPUT , 1=OUTPUT
;
;PORTB0 = 1 = NOT USED IN THIS APPLICATION, SET AS OUTPUT
;PORTB1 = 1 = OC0B = PWM TO MOSFET GATE
;PORTB2 = 1 = NOT USED IN THIS APPLICATION, SET AS OUTPUT
;PORTB3 = 0 = ADC INPUT
;PORTB4 = 1 = NU2 = GOES HIGH WHEN ADC IS READING
;PORTB5 = 0 = MPU RESET PIN
;PORTB6 = 1 = NOT ON THIS MCU BUT SET AS OUTPUT
;PORTB7 = 1 = NOT ON THIS MCU BUT SET AS OUTPUT
;
	ldi TEMP,0b11010111 ; Direction Port B
	out DDRB,TEMP
;
;
;set port pins
	SBI PORTB, NU1		;NOT USED
	CBI PORTB, NU2		;USED TO WATCH ADC PULSE
	SBI PORTB, NU3		;NOT USED
;
;
;
;
;
;================================
;    ENABLE THE ADC PROCESS
;================================
;
;
ENABADC:
	ldi TEMP, 0b00100011	;select ADC3 on PB3 as single ended ADC input
							;set ADLAR bit for 8 bit conversion
	out ADMUX, TEMP
	ldi TEMP, 0b10000011	;ADC enabled and prescaller is 2=00 8=11
	out ADCSRA, TEMP
	ldi TEMP, 0b00001000	;disable digital input on ADC pin
	out DIDR0, TEMP
;
;
;
;
; =================================
; =================================
; =================================
;         MAIN PROGRAM
; =================================
; =================================
; =================================
;
;
;
;========================================================
;  SETUP THE TIMER COUNTER FOR PHASE CORRECT PWM MODE
;========================================================
;
;
;CT0 in Phase Correct PWM Mode.
;Generating a Waveform:
;OCR0A as TOP, OCR0B as compare, OC0B as output.
;Vary OCR0A to change cycle time.
;
;Vary OCR0A to change cycle time.
;
loop_bak:
	rcall READ_ADC			;read input voltage and store as a 8 bit value at ADCH
;
;Set OCR0A (TOP) and OCR0B (bottom)
	ldi TEMP, ADCH
   	out OCR0A, TEMP			;Set the TOP limit as the ADCH value
  	ldi TEMP, 1
  	out OCR0B, TEMP			;set the BOTTOM limit to ground
;
;
;Configure Timer/Counter 0
;Select Phase Correct PWM Mode, and OCR0A as TOP. Use WGM Bits.
;WGM Bits are in TCCR0A and TCCR0B. Any previous settings are cleared.
;
;
;OC0B LO when COUNT = OCR0B upcounting, HI when COUNT = OCR0B downcounting.
	ldi TEMP,0b00100001
	out TCCR0A,TEMP 		; TCCR0A = 0b10000001 clear OC0A/OC0B on compare match, WGM00 = 1, WGM01 = 0
							; select COM0B1 output mode
	ldi TEMP,0b00001101
	out TCCR0B,TEMP 		; TCCR0B = 0b00001001 set clock divide by 1024, WGM02 = 1 = mode 5
;
;
	rjmp loop_bak
;
;
;
;
;
;
;===================================
;READ THE ADC INPUT AND CONVERT TO
;A 8 BIT VALUE. 00000000 = 0V
;11111111 = 5v
;RESULTS ARE AUTOMATICALLY STORED
;IN ADCH REGISTER
;===================================
;
;
READ_ADC:
	sbi portb, NU2			;turn on "ADC running" pulse on pin 3 of MCU, WATCH WITH SCOPE
	sbi ADCSRA, ADSC  		;start conversion ADSC to 1 (zeros when done)
loopX:   
	sbic ADCSRA, ADSC  		;ADSC bit goes low when conversion complete
 	rjmp loopX         		;not complete, keep checking
	cbi portb, NU2			;turn off "ADC running" pulse on pin 3 of MCU
	ret
;
;
;
;
;
;
;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

if I am truly blocked from participating from this forum I would sure like to know why.

I absolutely guarantee that you will not have been. The only people banned here are spammers and one very famous person. Such banning is actually done using an IP block so you wouldn't even have got as far as dialogs about cookies if you were truly banned.

(however it is wise to enable cookies for this site as that's how it tracks which threads you have read and which you haven't).

Moderator