Sida 4 av 5
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 19:25:27
av Glattnos
Skrev du detta...
Kod: Markera allt
if(freqCounter == SERVO_FREQ){
SERVO_PORT |= (1<<SERVO_CMD_PIN);
OCR1C = 255 - ActiveServoPositionPulse;
}
...som en variant av detta som jag skrev...
Kod: Markera allt
if(Counter == SERVO_FREQ){
SERVO_PORT |= (1<<SERVO_PIN);
TCNT0 = 255 - ServoPosition;
}
Bara så att du är observant på att det inte funkar helt likadant. I mitt exempel blir pulsens längd "ServoPosition"(högre värde, längre puls) medan i din kod blir pulsens längd "255 - ActiveServoPositionPulse"(högre värde, kortare puls).
Sen borde du använda...
...istället för...
...fast det har förmodligen inte med felet att göra utan funkar nog ändå
Edit: Förlåt, CTC1 är rätt
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 19:36:57
av Glattnos
Sen ska det väl vara OCR1A överallt där du har OCR1C?
Edit: Förlåt igen, jag gräver ju i fel datablad.
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 19:54:51
av Magnus_K
Ska ta en kik lite senare på det du skrivit Glattnos. Dock fungerar själv pulsningen kanon just nu så vill inte röra något timer-relaterat förrän jag löst det nuvarande problemet.
Är det alltid ok att göra så här?
uint16_t = uint8_t;
dvs flytta värdet från en mindre variabel till en större? Blir värdet alltid det samma i dom båda då?
EDIT: Blä. Provade map() funktionen men det fungerar inte heller. Måste vara något med ADC:n och att analogRead() inte fungerar i mitt program.
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 20:20:50
av ffredrik
Din kod här är helt korrekt:
ServoPositionOpenCal = analogRead(SERVO_OPEN_PIN);
ServoPositionOpenCal = (ServoPositionOpenCal >> 6) & 0xF;
Förmodligen får du för lågt värde från ADn så resultatet blir 0.
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 20:28:52
av Glattnos
Jag petade lite i koden men kan inte testa den själv.
Kod: Markera allt
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define F_CPU 8000000UL
#define SERVO_PORT PORTB
#define SERVO_CMD_PIN PB0
#define SERVO_CLOSE_PIN PB3
#define SERVO_OPEN_PIN PB4
#define RADIO_OPEN_PIN PB1
#define RADIO_CLOSE_PIN PB2
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens
volatile uint8_t freqCounter = 0;
volatile uint16_t calCounter = 0;
volatile uint8_t calState = 1;
volatile uint8_t ServoPositionClose = 150;
uint8_t ServoPositionCloseCal = 0; // 8-bit value
volatile uint8_t ServoPositionOpen = 146;
uint16_t ServoPositionOpenCal = 0;
uint8_t ActiveServoPositionPulse = 148; // 161 = 1500us, 135 = 1900us
static inline void initTimer1(void)
{
TCCR1 |= (1 << CTC1); // clear timer on compare match
TCCR1 |= (1 << CS13); // | (1 << CS11) | (1 << CS10); //clock prescaler
OCR1C = 255; // compare match value
TIMSK |= (1 << OCIE1A); // enable compare match interrupt
ADMUX |= (1 << ADLAR) | 0b10; //ADC Left adjusted, ADC-pin PB4
ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADPS2);
}
ISR(TIMER1_COMPA_vect)
{
freqCounter++;
if(calState){
calCounter++;
if(calCounter >= 0xFFFF){
calState = 0;
}
}
if(freqCounter == SERVO_FREQ){
SERVO_PORT |= (1<<SERVO_CMD_PIN);
OCR1C = 255 - ActiveServoPositionPulse;
}
if(freqCounter > SERVO_FREQ){
SERVO_PORT &= ~(1<<SERVO_CMD_PIN);
freqCounter = 0;
OCR1C = 105;
}
}
int main(void)
{
// initializations
pinMode(SERVO_CMD_PIN, OUTPUT);
pinMode(SERVO_CLOSE_PIN, INPUT);
pinMode(SERVO_OPEN_PIN, INPUT);
pinMode(RADIO_OPEN_PIN, INPUT);
pinMode(RADIO_CLOSE_PIN, INPUT);
initTimer1(); // initialize timer registers
sei(); // enable interrupts
while(1)
{
if(calState){
ServoPositionOpenCal = ADCH; //Read ADCH
ServoPositionOpenCal = (ServoPositionOpenCal >> 4); //Shift 4 steps
}
_delay_ms (1000);
ActiveServoPositionPulse = ServoPositionOpen + ServoPositionOpenCal;
}
}
Jag är inte helt säker på att man kan använda Arduino-kommandon, jag har provat att byta ut det som har med ADC:n att göra men det finns risk att det krockar med Arduinos "dolda kommandon".
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 20:31:30
av Magnus_K
@ffredrik:Har trott något sånt också men då blink-programmet fungerade så klockrent och delay(adc-värde) stämde så precis så uteslöt jag det.
Efter blod, svett och tårar (mest tårar) har jag fått igång det nu. Tyvärr har jag inte en aning om varför det blir så här.
Tyckte mig komma fram till det verkligen är något tok med analogRead() i mitt program så sökte runt på nätet efter kod som kunde hjälpa mig att konfigurera ADC:n manuellt och göra en manuell utläsning (eller vad man ska kalla det).
Hittade lite kod och testade och visst, fungerade hur bra som helst på första försöket.
Det jag alltså inte har en aning om är varför analogRead() inte fungerar i mitt program. Är det för att man stället om timers och annat?
Koden nedan fungerar alltså precis som tänkt. Obs. Här läser jag bara ut 8-bitar så skiftar enbart bort 4 LSBs.
EDIT: Hehe, du tänkte precis rätt
Glattnos 
Ska jämföra programmen lite och se skillnaderna. Ditt program är mycket renare än mitt ihopklistrade klabb.
Kod: Markera allt
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define F_CPU 8000000UL
#define SERVO_PORT PORTB
#define SERVO_CMD_PIN PB0
#define SERVO_CLOSE_PIN PB3
#define SERVO_OPEN_PIN PB4
#define RADIO_OPEN_PIN PB1
#define RADIO_CLOSE_PIN PB2
#define SERVO_FREQ 11 //Högre siffra ger lägre frekvens
volatile uint8_t freqCounter = 0;
volatile uint16_t calCounter = 0;
volatile uint8_t calState = 1;
static uint8_t ServoPositionClose = 150;
volatile uint16_t ServoPositionCloseCal = 0;
static uint8_t ServoPositionOpen = 146;
volatile uint16_t ServoPositionOpenCal = 0;
volatile uint8_t ActiveServoPositionPulse = 148; // 161 = 1500us, 135 = 1900us
static inline void initTimer1(void)
{
TCCR1 |= (1 << CTC1); // clear timer on compare match
TCCR1 |= (1 << CS13); // | (1 << CS11) | (1 << CS10); //clock prescaler
OCR1C = 255; // compare match value
TIMSK |= (1 << OCIE1A); // enable compare match interrupt
}
ISR(TIMER1_COMPA_vect)
{
freqCounter++;
if(calState){
calCounter++;
if(calCounter >= 0xFFFF){
calState = 0;
}
}
if(freqCounter == SERVO_FREQ){
SERVO_PORT |= (1<<SERVO_CMD_PIN);
OCR1C = 255 - ActiveServoPositionPulse;
}
if(freqCounter > SERVO_FREQ){
SERVO_PORT &= ~(1<<SERVO_CMD_PIN);
freqCounter = 0;
OCR1C = 105;
}
}
int main(void)
{
// initializations
pinMode(SERVO_CMD_PIN, OUTPUT);
pinMode(SERVO_CLOSE_PIN, INPUT);
pinMode(SERVO_OPEN_PIN, INPUT);
pinMode(RADIO_OPEN_PIN, INPUT);
pinMode(RADIO_CLOSE_PIN, INPUT);
initTimer1(); // initialize timer registers
sei(); // enable interrupts
initADC();
while(1)
{
if(calState){
ADCSRA |= (1 << ADSC); // start ADC measurement
while (ADCSRA & (1 << ADSC) ); // wait till conversion complete
ServoPositionOpenCal = ADCH >> 4;
}
_delay_ms (1000);
ActiveServoPositionPulse = ServoPositionOpen + ServoPositionOpenCal;
}
return 0;
}
void initADC()
{
ADMUX =
(1 << ADLAR) | // left shift result
(0 << REFS1) | // Sets ref. voltage to VCC, bit 1
(0 << REFS0) | // Sets ref. voltage to VCC, bit 0
(0 << MUX3) | // use ADC2 for input (PB4), MUX bit 3
(0 << MUX2) | // use ADC2 for input (PB4), MUX bit 2
(1 << MUX1) | // use ADC2 for input (PB4), MUX bit 1
(0 << MUX0); // use ADC2 for input (PB4), MUX bit 0
ADCSRA =
(1 << ADEN) | // Enable ADC
(1 << ADPS2) | // set prescaler to 64, bit 2
(1 << ADPS1) | // set prescaler to 64, bit 1
(0 << ADPS0); // set prescaler to 64, bit 0
}
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 20:47:30
av Glattnos
Då satt vi och gjorde precis samma sak. Jag tror inte att DigitalRead() funkar på en ATiny45. Bra att du fått det att funka

Du behöver inte starta ADC convertion och vänta varje gång du ska läsa, ADCn går i FreeRunning mode om man inte sätter någon av ADTS-bitarna. Kolla hur jag lagt in det i initTimer1 rutinen, nu vet jag inte om det funkar eftersom jag inte har en ATiny att testa med.
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 20:51:49
av Magnus_K
Du är ju fantastisk
Glattnos! Satt och läste om freerunning-läget precis och du svarade på exakt vad jag sökte
Kommer finlira lite nu med hjälp av din kod men onekligen var det detta som felade.
Då jag nyttjar två pottar, en för att kalibrera öppningsvinkel, och en för stängnigsvinkeln, så blir det lite kul utmaning att få till det här i koden. Men nu har jag förutsättningarna i alla fall.
Tack så hemskt mycket för all tid du/ni lagt ner

Lägger självklart upp det färdiga programmet sen. Kanske alltid hjälper någon.
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 23:02:34
av Magnus_K
Nä det vill inte. Tänk vad tid man lägger ner på att harva bland dessa kodsnuttar och det blir aldrig som man vill. Suck och stön.
Det är så konstigt. Vill bara byta kanal i MUX:en så jag får värdet från ADC3 istället för ADC2.
Utläsningen och allt som har med ADC2 fungerar klockrent men 3:an vill sig inte.
Som jag förstår från databladet så ändrar man bara i MUX-registret till rätt kanal, ger ADC:n tid för en ny konvertering, och sen är det bara att läsa ur värdet. Men icke...
Vet ni om man ska ändra något mer när jag byter kanal? Tycker inte jag hittar något om det i databladet så borde gå bra...
Kod: Markera allt
while(1)
{
if(calState){
ADCSRA |= (1 << ADSC); // Start the ADC conversion
while (ADCSRA & (1 << ADSC) ); // Wait until conversion complete
ServoPositionOpenCal = ADCH; // Write ADC-value
ServoPositionOpenCal = (ServoPositionOpenCal >> 4) & 0xF; // Decrease resolution
ADMUX = (1 << MUX0) | (1 << MUX1); // Change to MUX to ADC3
ADCSRA |= (1 << ADSC); // Restart the ADC conversion
while (ADCSRA & (1 << ADSC) ); // Wait until conversion complete
ServoPositionCloseCal = ADCH; // Write ADC-value
ServoPositionCloseCal = (ServoPositionCloseCal >> 4) & 0xF; // Decrease resolution
ADMUX = (0 << MUX0) | (1 << MUX1); // Change to MUX to ADC2
}
_delay_ms (1000);
ActiveServoPositionPulse = ServoPositionOpen - ServoPositionOpenCal;
_delay_ms (1000);
ActiveServoPositionPulse = ServoPositionClose + ServoPositionCloseCal;
}
Re: Arduino ATtiny45 servoproblem
Postat: 4 mars 2018, 23:20:27
av Magnus_K
Vilken slamkrypare... Då jag byter kanal i MUX:en så nollas tydligen ADLAR vilket gör att ADC-värdet blir högerjusterat istället (säger man så?).
Oavsett så löstes problemet genom att lägga till
| (1 << ADLAR) när jag byter kanal. Pust...
EDIT: Eller ja, sättet jag manipulerar ADMUX-registret skriver även över (nollar) ADLAR-biten. Värt att tänka på... Tur att dom satt Vcc som Vref 00 i mitt fall

Re: Arduino ATtiny45 servoproblem
Postat: 5 mars 2018, 01:06:45
av Glattnos
Du kan köra en ADC-avbrottsrutin istället när du ska ta in värdet från flera pinnar, då kan du i avbrottsrutinen rotera mellan de pinnar du vill läsa och spara respektive värde i dedikerade variabler som du sedan använder i ditt huvudprogram. Då kan man läsa alla tillgängliga ADC-pinnar utan att det tar någon extra tid i huvudprogrammet
Och helt rätt, istället för..
Kod: Markera allt
ADMUX = (1 << ADLAR) | (0 << MUX0) | (1 << MUX1);
...så kan du ju...
Kod: Markera allt
ADMUX |= (1 << MUX1); //Set bit
ADMUX &= ~(1 << MUX0); //Clear bit
Det är bra att ta för vana att bara ändra de bitar som man faktiskt ämnar ändra för att hindra konstiga fel som inträffar när man råkar ändra viktiga bitar av misstag.
Re: Arduino ATtiny45 servoproblem
Postat: 5 mars 2018, 18:20:39
av Glattnos
Detta funkar hyffsat bra. Du kan lägga in kod under ADC-avbrottsrutinen som byter ADC-pinne och sparar i en egen variabel.
Dock har jag skrivit koden för en ATtiny13 som var det närmaste jag hade hemma, men koden funkar i alla fall bra på den och jag tror inte att det är stor skillnad mot en ATtiny45.
Denna kod ger nästan full 8-bitars upplösning på servo-positionen. Det är lite olika på olika servon så man kan ju få justera vissa värden. Ett Hitec HS-422 som jag hade liggande funkar uppenbarligen bra i spannet 700-2400us.
Kod: Markera allt
#include <avr/io.h>
#include <avr/interrupt.h>
#define SERVO_PORT PORTB
#define SERVO_DDR DDRB
#define SERVO_PIN PB3
#define SERVO_FREQ 20 //Frequency-period in ms
#define TIMER0_MS 150 //150 for 9.6 MHz or 125 for 8 MHz gives 1ms
#define SHORTEST_SERVO_PULSE 90 //90 for 9.6 MHz gives 600us
volatile uint8_t Counter = 0;
volatile uint8_t ServoPosition;
volatile uint8_t ADC_Value;
ISR(ADC_vect){
ADC_Value = ADCH; //Read and store the ADC value
ADCSRA |= (1 << ADSC); //Start new conversion
}
ISR(TIM0_COMPA_vect){
Counter++; //Increase Counter by 1
if(Counter == SERVO_FREQ){ //If Counter equals to SERVO_FREQ...
SERVO_PORT |= (1<<SERVO_PIN); //...then set the servopin...
OCR0A += SHORTEST_SERVO_PULSE; //...and make next compare match in 600us
}
else if(Counter == SERVO_FREQ + 1){ //If this was the ShortestServoPulse..
OCR0A += ServoPosition; //...then make next compare match in "ServoPosition"
}
else if(Counter >= SERVO_FREQ + 2){ //If this is the end of the pulse...
SERVO_PORT &= ~(1<<SERVO_PIN); //...then clear the servopin...
OCR0A += TIMER0_MS; //...and make next compare match in 1ms
Counter = 0; //Restart by clearing Counter
}
else OCR0A += TIMER0_MS; //Else make next compare match in 1ms
}
void setup(){
//Setup I/O
SERVO_DDR = (1<<SERVO_PIN);
//Setup Timer0
TCCR0B = (0<<CS02)|(1<<CS01)|(1<<CS00); //Precsale clk/64
OCR0A = TIMER0_MS; //Make a compare match in 1ms
TIMSK0 = (1<<OCIE0A); //Enable compare match Interrupt
//Setup ADC
ADMUX = (1 << ADLAR) | (1<<MUX1); //ADC Left adjusted, ADC-pin PB4
ADCSRA = (1 << ADEN) | (1<<ADIE) | (1 << ADSC) | (1 << ADPS2); //Enable ADC, Enable ADC Interrupt, StartConversion, Set prescaler
//Enable global interrupts
asm("sei");
}
int main(){
setup();
while(1){
if(ADC_Value > 5 && ADC_Value < 250) ServoPosition = ADC_Value; //Limit the range
}
}
Re: Arduino ATtiny45 servoproblem
Postat: 6 mars 2018, 12:52:25
av Magnus_K
Tack (igen)
Glattnos!
Ska sätta mig med det här ikväll igen och försöka modifiera din kod.
Kommer klura under dagen men om man är en jäkel på det här programmering, borde det inte gå att få till så man i början sätter pulslängd för max moturs och pulslängd för max medurs.
Baserat på dessa värden så justeras klockan så man får max antal bitar på det spannet?
Alltså väldigt "dynamiskt" och anpassningsbar kod beroende på vilket servo du har.
Re: Arduino ATtiny45 servoproblem
Postat: 6 mars 2018, 20:51:27
av Glattnos
Det är en sån lösning jag skulle vilja få till men samtidigt vill jag inte belasta processorn med onödigt mycket konverteringar av värden. Men jag är bara hobby-kodare

Vad menar du med "så justeras klockan"? Går det?
Re: Arduino ATtiny45 servoproblem
Postat: 6 mars 2018, 21:59:09
av Magnus_K
Du gör väldigt bra ifrån dig för att "bara" vara hobby-kodare!
Klockan var nog fel, menade kanske mer timer/prescaler men det blir för rörigt i min skalle när man måste hålla ~50Hz samtidigt.
Det är bra som det är
