Управляем всем с помощью регистров cдвига.

ich
Offline
Зарегистрирован: 10.06.2012

Доброго всем время суток. Прошу сильно не пинать, за то-что создал новую тему а не начал задовать вопросы тут: (регистры сдвига).

Я думаю что кучу моих вопросов, которые у меня назрели, желательно отпочковать от основной темы. Если администрация не согласна с этим, прошу перенисти этот топик в (регистры сдвига).

Начну как многие форумчане словами: "мол я начинающий, с ардуиной сильно не знаком и т.д. и т.п. бла-бла-бла."

Хватит болтавни, перейдём к сути.

Что имеется: 74HC165, 74HC595, Servo RS-2, HC-SR04, LED's, кнопки, Arduino Uno.

Что хочится: Создать более или мение универсальную возможность управлять перечисленными деталями с помощью 5 проводов (2 для 74HC165, 2 для 74HC595, 1 общий для такта). Получаем "для начала" 8-входов и 8-выходов. Все входы и выходы должны работать "полноценно" т.е. как будьто это ардуиновские входы/выходы.

Проблемма:  "полноценно" Понимаю что это не возможно, так как мне тяжело обмануть регистровые задержки. А именно это и является для меня самой большой проблемой.

Что я уже напоял:

Что я уже на програмировал:

MultiIO.h

/*
 * MultiIO.h
 *
 *  Created on: 06.10.2012
 *      Author: Ich
 */

#ifndef MULTIIO_H_
#define MULTIIO_H_
#include "Arduino.h"
/*
 * uint8_t = 8 Bit = 256 Kombinationen
 *
 */
#define BIT_COUNT 8 // Anzahl von Bit's pro IC
#define SERVO_MIN_PULSE 350
#define SERVO_MAX_PULSE 2150
#define SERVO_REFRESH 20

class MultiIO{
public:
	// Allgemeine Konstruktor
	MultiIO(uint8_t clock_clk);
	// Für beide 165 und 595
	MultiIO(uint8_t clock_clk, uint8_t data_DS, uint8_t latch_ST,
			uint8_t pload_SH, uint8_t data_QH,
			uint8_t ic595_count = 1, uint8_t ic165_count = 1);
	// Nur für 595
	void init595(uint8_t data_DS, uint8_t latch_ST, uint8_t ic_count = 1);
	// Nur für 165
	void init165(uint8_t pload_SH, uint8_t data_QH, uint8_t ic_count = 1);
	// Taktgeber für beide (595 und 165)
	void initCLK(uint8_t clock_clk);
	// Schreiben in 595
	void shiftWrite(uint8_t writeValue);
	// Schreiben in 595
	void write(uint8_t pin, bool state);
	// Servo positionieren
	void writeToServo(uint8_t pin, uint8_t pos);
	// Lesen von 165
	uint8_t shiftRead();
	// Passives Lesen von bestimmte Pin. Es werden alte Registerdaten genutzt ohne shiftRead() zu aufrufen.
	bool readPassiv(uint8_t pin);
	// Lesen von bestimmte Pin. Bei jedem Aufruf wird shiftRead(); aufgerufen um aktuellste Zustand zu bekommen.
	bool read(uint8_t pin);
	// PulseIn timeout = 10000µs = 10ms
	unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 10);
	// Hollt Abstand bis zum Hinderniss in mm
	unsigned long getDdistance(uint8_t trig, uint8_t echo);
private:
	uint8_t i;				// Zaehlvariable für Schleifen
	uint8_t outPinsCount;	// Anzahl von Ausgabepins (für ein 595 ist 8, für n 595 ist n*8) "Anzahl von Bit"
	uint8_t inPinsCount;	// Anzahl von Eingabepins (für ein 165 ist 8, für n 165 ist n*8) "Anzahl von Bit"
	uint8_t writeValue;		// Bitfolge die in Register geschrieben wird
	uint8_t readValue;		// Bitfolge die aus Register gelesen ist
	uint8_t data_DS;		// serial data input (595)
	uint8_t latch_ST;		// storage register clock input (595)
	uint8_t clock_clk;		// clock input (LOW-to-HIGH edge-triggered) (595_165)
	uint8_t pload_SH;		// asynchronous parallel load input (active LOW) (165)
	uint8_t data_QH;		// serial output from the last stage (165)
};

#endif /* MULTIIO_H_ */

 

MultiIO.cpp

 

/*
 * MultiIO.cpp
 *
 *  Created on: 06.10.2012
 *      Author: Ich
 */

#ifndef MULTIIO_CPP_
#define MULTIIO_CPP_

#include "MultiIO.h"

// Algemeine Konstruktor
MultiIO::MultiIO(uint8_t clock_clk){
	initCLK(clock_clk);
}

// Für beide 165 und 595
MultiIO::MultiIO(uint8_t clock_clk, uint8_t data_DS, uint8_t latch_ST,
			uint8_t pload_SH, uint8_t data_QH,
			uint8_t ic595_count, uint8_t ic165_count){
	initCLK(clock_clk);
	init595(data_DS, latch_ST, ic595_count);
	init165(pload_SH, data_QH, ic165_count);
}

// Initialisieren von 595
void MultiIO::init595(uint8_t data_DS, uint8_t latch_ST, uint8_t ic_count){
	this->writeValue = 0;
	this->outPinsCount = BIT_COUNT * ic_count; // Anzahl von Ausgabebits
	this->data_DS 	= data_DS;
	this->latch_ST	= latch_ST;
	pinMode(data_DS, OUTPUT);
	pinMode(latch_ST, OUTPUT);
}

// Initialisieren von 165
void MultiIO::init165(uint8_t pload_SH, uint8_t data_QH, uint8_t ic_count){
	this->readValue = 0;
	this->inPinsCount = BIT_COUNT * ic_count; // Anzahl von Eingabepins
	this->pload_SH	= pload_SH;
	this->data_QH	= data_QH;
	pinMode(pload_SH, OUTPUT);
	pinMode(data_QH, INPUT);
}
// Taktgeber für beide (595 und 165)
void MultiIO::initCLK(uint8_t clock_clk){
	this->clock_clk	= clock_clk;
	pinMode(clock_clk, OUTPUT);
}
// Schreiben in 595
void MultiIO::write(uint8_t pin, bool state){

	if(state){
		this->writeValue |=  (1 << pin);
	}else{
		this->writeValue &=  ~(1 << pin);
	}
	shiftWrite(writeValue);
}

// Schreiben in 595 alle Ausgänge gleichzeitig
void MultiIO::shiftWrite(uint8_t writeValue)
{
	this->writeValue = writeValue;
	digitalWrite(latch_ST, LOW);
	i = outPinsCount;
	do{
		digitalWrite(data_DS, !!(writeValue & (1 << ((i--)-1))));
		// Takt geben, um Änderungen vorzunehmen.
		digitalWrite(clock_clk, HIGH);
		digitalWrite(clock_clk, LOW);
	}while(i>0);

	digitalWrite(latch_ST, HIGH);
}
// Servo positionieren
void MultiIO::writeToServo(uint8_t pin, uint8_t pos){
	for(int i=0; i<20; i++){
	    this->write(pin, HIGH);
	    delayMicroseconds(map(pos,0,180,SERVO_MIN_PULSE,SERVO_MAX_PULSE));
	    this->write(pin, LOW);
	    delay(SERVO_REFRESH);
	 }
}
// Lesen von 165
uint8_t MultiIO::shiftRead(){
	readValue = 0;
	digitalWrite(pload_SH, LOW);
	digitalWrite(pload_SH, HIGH);

	for(i=0; i<inPinsCount; i++){
		readValue |= (digitalRead(data_QH) << ((inPinsCount-1)-i));
	    digitalWrite(clock_clk, HIGH);
	    digitalWrite(clock_clk, LOW);
	}
	return readValue;
}
// Passives Lesen von bestimmte Pin. Es werden alte Registerdaten genutzt ohne shiftRead() zu aufrufen.
bool MultiIO::readPassiv(uint8_t pin){
	return bitRead(readValue, pin);
}
// Lesen von bestimmte Pin. Bei jedem Aufruf wird shiftRead(); aufgerufen um aktuellste Zustand zu bekommen.
bool MultiIO::read(uint8_t pin){
	shiftRead();
	return readPassiv(pin);
}

// PulseIn
unsigned long MultiIO::pulseIn(uint8_t pin, uint8_t state, unsigned long timeout){

	// TimeOut
	unsigned long numloops = 0;
	unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

	// wait for any previous pulse to end
	while(this->read(pin) == state)
		if (numloops++ == maxloops)
			return 0;

	unsigned long width = 0;
	// wait for the pulse to start
	while (this->read(pin) != state)
		if (numloops++ == maxloops)
			return 0;

	// wait for the pulse to stop
	while (this->read(pin) == state) {
		if (numloops++ == maxloops)
			return 0;
			width++;
	}

	return clockCyclesToMicroseconds(width * 21 + 16);

	/*
	//check to see if the pin is already in the value state, if so, the pulse has already begun and the function needs to wait for the next one to "catch" the leading edge.
	//while(this->read(pin) == state);
	// wait for pin state
	while (this->read(pin) != state);

	unsigned long start = micros();

	// wait for pin to change state
	while (this->read(pin) == state);

	unsigned long end = micros();

	// return time in micro seconds
	return (end - start);
	*/
}

// Hollt Abstand bis zum Hinderniss in mm
unsigned long MultiIO::getDdistance(uint8_t trig, uint8_t echo){
	  this->write(trig, LOW);
	  delayMicroseconds(2);
	  this->write(trig, HIGH);
	  delayMicroseconds(10);
	  this->write(trig, LOW);

	  return this->pulseIn(echo, HIGH);
}

#endif // #ifndef MULTIIO_CPP_

Отпишусь сразу, на случай если эксперты забракуют мой код. На С++ не програмировал не разу, начал совсем недавно из за ардуинки. По этому любая конструктивная критика, только приветствуется.

Крик о помощи:

На данный момент я могу читать все пины одним разом shiftRead, читать каждый пин по одиночки read (основанно на предидущем), писать во все пины shiftWrite, писать в каждый пин по одиночки write (основанно на предидущем), крутить сервы writeToServo (не совсем идеально, плюс delay's всё тормозят) и последняя функция pulseIn (вообще тупик, работает но странно. Думаю проблемма из за потери времени в регистрах).

Как мне улучшить writeToServo, избавить от остоновки скетча? Моргание без delay думаю тут не поможет. Нужно как то с прерываниями. А вот как не знаю. Надеюсь на помощь "потомственных избоалятелей" от delay. ;-)

Как быть с pulseIn? Прнимаю что без остоновки скетча не обойтись, но это и не страшно, пускай остонавливается, главное получить валидные данные. Куда рыть, чтобы победить эту проблемму?

Ну и естественно эти все функции не должны друг другу мешать. Пока есть конфликты между shiftWrite и write, так как первая переписывает статус второй. Но это тоже пол беды, нужно просто во время пользования функциями не забывать о этой особенности.

Надеюсь что я доступно описал проблемму и вам не прейдётся обращатся к годалкакм и шаманам. :-)

maksim
Offline
Зарегистрирован: 12.02.2012

Могу посоветовать вам воспользоваться аппаратным SPI, у него при частоте МК 16 МГц частота линии тактирования может достигать 4 МГц, запись и чтение происходит одновременно.

ich
Offline
Зарегистрирован: 10.06.2012

Тоесть, Вы предлогаете зпбить на 74HC165 и 74HC595 и воспользоваться другой IC? Если да то каоую лучше посоветуете?

Почитав про SPI в интернете, сложилось мнение, что 74HC165 и 74HC595 можно тоже к этому отнести. Принцип та тот же. Или я что-то не допонял?

 

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Да, принцип тот же. И работать оно будет. И проводок сэкономите (микрухи должны сидеть на одном селекте, они же друг другу не мешают, одна передает, другая принимает) 

Еще поищите (я где-то натыкался) быстрый shiftOut - на управлении регистрами напрямую. Но вариант SPI - самый лучший.

maksim
Offline
Зарегистрирован: 12.02.2012

ich пишет:
Тоесть, Вы предлогаете зпбить на 74HC165 и 74HC595 и воспользоваться другой IC? Если да то каоую лучше посоветуете?
Нет, я предлогаю управлять 74HC165 и 74HC595 по интерфейсу SPI, а не функциями shiftOut/shiftIn. 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

 Статья на Робокрафте Подключаем кучу устройств к Arduino по 5 проводам - специально для Вас! :) Там как раз используют для этой цели SPI

leshak
Offline
Зарегистрирован: 29.09.2011

 > Моргание без delay думаю тут не поможет

Ну полностью "Решить проблему" может и не сможет. Но вот, все-таки избавлятся от delayMicroseconds - нужно.

Либо, все-таки по принципу "мигаем без delay". Либо смотреть в сторону таймеров.

Можно еще погуглить  - не вы первый задались сервой рулить через сдвиговые 

Например вот:

http://letsmakerobots.com/node/3829

Не совсем "ардуино", но родственный AVR/C - подсмотреть "идею" - можно.

Возможно и "ардуино way" нагуглите (в youtube видел, но там нет ссылки на "как это сделали").

А вообще, IMHO, через сдвиговые PWM изображать - слишком гиморно :(  И накладывает кучу ограничений на остальную логику скетча.

Есть специально для этого предназначенные микрухи (дороже правда существенно). Рулятся, как правило, по I2C. Но вот они, как раз умеют приять команду типа "установи на такой-то своей ноге - такой PWM". И все. И сами его там генерят/держат. Не напрягая, по этому поводу, сам микроконтроллер.

 

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Как вариант, идея:

  1. массив на 8 байт, каждый из которых представляет собой значение PWM для одной ноги регистра.
  2. Забираем у дуины таймер1 (библиотека TimerOne), делаем на нем прерывание с интервалом  2/256 ms
  3. В прерывании увеличиваем счетчик на 1, сравниваем с массивом, устанавливаем нужные биты в буферном байте, выводим байт в регистр.

Вот только сколько ему времени на это потребуется, вызывается то прерывание через 8 мкс... Не успеет... Разве что ШИМы не на 500 Гц делать, а на 50 хотя бы. Тоже не успеет...

Понимаю, идея туповата, но может на что-нибуть натолкнет...

Alexander89
Offline
Зарегистрирован: 02.09.2012

Красивую вы платку выложили.

Есть ли печатка?

Где продают такие готовые и по чем?

 

 

ich
Offline
Зарегистрирован: 10.06.2012

Спасибо всем за помощь и наводки. Понятия не имею где были мои глаза, когда я читал эту статью. Возможно не прешлось бы и тему создавать.

Для начало подружу всю эту бедень с Servo ну а потом уж и за pilseIn возьмусь, вот тогда-то и вернусь для задования очередных вопросов.

Alexander89 пишет:

Красивую вы платку выложили.

Есть ли печатка?

Где продают такие готовые и по чем?

Платка срисованна 1 в 1 с той что я споял, рисовал с помощью MSPaint  (самому тоже нравится ).

Обошлось мне это всё не больше 5€, схемы конкретной нету так как по Даташидам ориентировался.

Могу и Вам такую скрутить, это уже зависит от того как просить будите .

ich
Offline
Зарегистрирован: 10.06.2012

Спасибо всем за подсказки, теперь мой MultiIO работает на прямую, без digitalWrite/Read, и правдо стало на много быстрей, это даже на глаз заметно Oo. Servo тоже работает таперь без остановки скетча, для эого я воспользовался трюком с манипуляцией Servo.h из этой статьи.

Ну вот добрался и до pulseIn.

Для начало я написал обыкновенный скетч, без сдвиговиков, пользуясь штатными функциями.

int pingPin = 13;
int inPin = 12;
 
void setup() {
  Serial.begin(9600);
  pinMode(pingPin, OUTPUT);
  pinMode(inPin, INPUT);
}
 
void loop(){   
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(pingPin, LOW);
  
  Serial.print(pulseIn(inPin, HIGH));
   
  delay(100);
}

 

Вот что у меня показал монитор.

 

202042
202022
202019
202010
201994
201980
201980
201975
1280
1163
1161
1161
1161
1152
1157
1157
1161
1161
1158
1158
1152
1154
1125
981
879
675
539
457
397
340
323
326
358
329
289
230
245
228
196
191
188
209
209
195
203
218
221
212
214
212
172
187
184
191
216
209
186
158
128
111
119
132
125
119
125
125
125
125
142
174
172
172
149
129
146
268
550
1037
1678
201824
201800
201784
201772
2484
2397
201759
201749
201753
201745
201738
201734
201727
201720
201709
201702
201704
201695
201688
201681
201675
201678
201666
201664
201656
201654
201645
201644
201637
201633
201619
201620

Т.е. отталкиваясь от полученных данных мне надо реализовать функцию которая хотябы преблезительно делает тоже самое. Не долго думая я залез в штатную функцию pulseIn и немного её модифицируя воткнул в свой MultiIO.

Как это выглядет:

// PulseIn
// default timeout = 1000000L
unsigned long MultiIO::pulseIn(uint8_t pin, uint8_t state, unsigned long timeout){

	// TimeOut
	// Для того чтобы избежать полной остоновки сктча в случае отсутствия сигнала.
	unsigned long numloops = 0;
	unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

	// wait for any previous pulse to end
	// Тут игнарируем все импульсы каторые уже соответствуют state
	while(this->read(pin) == state)
		if (numloops++ == maxloops) // Если сильно долго ждём
			return 0; // Останавливаем процесс, возвращая 0

	unsigned long width = 0; // Сюда сохраняем длинну импульса
	
	// wait for the pulse to start
	// Ждём отрецательного сигнала, каторый и является стартоваы сигналом для 
	// замера импульса.
	while (this->read(pin) != state)
		if (numloops++ == maxloops) // Если сильно долго ждём
			return 0; // Останавливаем процесс, возвращая 0

	// wait for the pulse to stop
	while (this->read(pin) == state) {
		if (numloops++ == maxloops) // Если сильно долго ждём 
			return 0; // Останавливаем процесс, возвращая 0
		width++; // Считаем количество/длинну сигнала
	}

	return clockCyclesToMicroseconds(width * 21 + 16);
}

Вот что выдаёт эта функция работая уже с регистрами и через мою MultiIO

994
0
130
4
22
22
20
393
0
272
175
23
20
20
20
20
20
19
20
20
18
384
0
272
0
22
403
0
272
0
22
20
20
20
20
19
351
0
272
18
0
8
19
19
19
20
22
22
23
11
23
472
272
272
272
272
119
0
272
0
29
272
0
272
0
29
272
0
272
0
29
272
0
272
0
29
272
0
272
0
29
272
0
272
0
28
272
0
272
0
28
272
0
272
0
28
272
0
272
0
29
272
0
272
0
29
272
0
272
0
28
272
0
272
0
28
272
272
272
272
267
124
0
272
0
29
272
0
541
127

 Воошпе бред какой-то :-(. Я даже и не знаю что делать.

Разковырял ещё парачку данных, но они мне тоже не очём не говорят. Например microsecondsToClockCycles(timeout=1000000L) из pulseIn в оригинальном случае возвращает: 16000000 а в моём 3115098. Как так? Ведь эта функция получает одинаковые данные, да и как я понял в итоге она не делает ни чего другого как:

# define F_CPU 1000000UL

#define microsecondsToClockCycles(a) ( ((a) * (F_CPU / 1000L)) / 1000L )

Т.е. Я задаю 1000000L => (((1000000L)*(1000000UL / 1000L )) / 1000L)  = 1000000 а не 16000000 и уж точно не 3115098.

В интернэте искал, поисковиком тоже пользовался. Ничего стоющего не нашол.

Кроме вот этого кода, который тоже не к чему ожидаемому не привёл.

	//check to see if the pin is already in the value state, if so, the pulse has already begun and the function needs to wait for the next one to "catch" the leading edge.
	while(this->read(pin) == state);
	
	// wait for pin state
	while (this->read(pin) != state);

	unsigned long start = micros();

	// wait for pin to change state
	while (this->read(pin) == state);

	unsigned long end = micros();

	// return time in micro seconds
	return (end - start);

 

Что мне делать, как заставить это всё работать?

maksim
Offline
Зарегистрирован: 12.02.2012

Вы должны понимать, что когда у вас работает функция pulseIn, то все остальное останавливается в том числе и SPI, если конечно у вас работа с регистрами не в прерывании по таймеру сидит, вам нужно что бы функция pulseIn не тормазила основную программу, двигайтесь вот в этом напрвлении Мигаем светодиодом без delay(), скорее всего в любом случае пострадает точность, но подругому никак.

ich
Offline
Зарегистрирован: 10.06.2012

То что всё остольное остонавливается, я понимаю. На данный момент у меня задача реализовать полную копию pulseIn которая работает через регистры (то что точность пострадает я с этим смерился. Но не так же как у меня на данный момент). SPI у меня не остонавливается, так как моя pulseIn юзает мою функцию this->read(pin) а она читает состояние не ардуиновского пина, а пин регистра. Т.е. моя this->read(pin) работает именно с SPI.

Когда я пишу SPI, я не имею в веду библиотеку SPI, я имею в виду прямой доступ на ардуино с помощью PINB / PIND.

Вот как у меня выглядит весь MultiIO. Отличается от исходной добавленными функциями digitalWriteDirect и digitalReadDirect (Для увеличения скорости обращения). Они заменяют стандартные функции.

/*
 * MultiIO.cpp
 *
 *  Created on: 06.10.2012
 *      Author: Ich
 */

#ifndef MULTIIO_CPP_
#define MULTIIO_CPP_

#include "MultiIO.h"
static const uint8_t PORT_ARRAY[] = {/*PORTD*/0,1,2,3,4,5,6,7,/*PORTB*/0,1,2,3,4,5};

// Algemeine Konstruktor
MultiIO::MultiIO(uint8_t clock_clk){
	initCLK(clock_clk);
}

// Für beide 165 und 595
MultiIO::MultiIO(uint8_t clock_clk, uint8_t data_DS, uint8_t latch_ST,
			uint8_t pload_SH, uint8_t data_QH,
			uint8_t ic595_count, uint8_t ic165_count){
	initCLK(clock_clk);
	init595(data_DS, latch_ST, ic595_count);
	init165(pload_SH, data_QH, ic165_count);
}

// Initialisieren von 595
void MultiIO::init595(uint8_t data_DS, uint8_t latch_ST, uint8_t ic_count){
	this->writeValue = 0;
	this->outPinsCount = BIT_COUNT * ic_count; // Anzahl von Ausgabebits
	this->data_DS 	= data_DS;
	this->latch_ST	= latch_ST;
	pinMode(data_DS, OUTPUT);
	pinMode(latch_ST, OUTPUT);
}

// Initialisieren von 165
void MultiIO::init165(uint8_t pload_SH, uint8_t data_QH, uint8_t ic_count){
	this->readValue = 0;
	this->inPinsCount = BIT_COUNT * ic_count; // Anzahl von Eingabepins
	this->pload_SH	= pload_SH;
	this->data_QH	= data_QH;
	pinMode(pload_SH, OUTPUT);
	pinMode(data_QH, INPUT);
}
// Taktgeber für beide (595 und 165)
void MultiIO::initCLK(uint8_t clock_clk){
	this->clock_clk	= clock_clk;
	pinMode(clock_clk, OUTPUT);
}
// Schreiben in 595
void MultiIO::write(uint8_t pin, bool state){

	if(state){
		this->writeValue |=  (1 << pin);
	}else{
		this->writeValue &=  ~(1 << pin);
	}
	shiftWrite(writeValue);
}

// Schreiben in 595 alle Ausgänge gleichzeitig
void MultiIO::shiftWrite(uint8_t writeValue)
{
	this->writeValue = writeValue;
	digitalWriteDirect(latch_ST, LOW);
	i = outPinsCount;
	do{
		digitalWriteDirect(data_DS, !!(writeValue & (1 << ((i--)-1))));
		// Takt geben, um Änderungen vorzunehmen.
		digitalWriteDirect(clock_clk, HIGH);
		digitalWriteDirect(clock_clk, LOW);
	}while(i>0);

	digitalWriteDirect(latch_ST, HIGH);
}
// Servo positionieren
void MultiIO::writeToServo(uint8_t pin, uint8_t pos){
	for(int i=0; i<20; i++){
	    this->write(pin, HIGH);
	    delayMicroseconds(map(pos,0,180,SERVO_MIN_PULSE,SERVO_MAX_PULSE));
	    this->write(pin, LOW);
	    delay(SERVO_REFRESH);
	 }
}
// Lesen von 165
uint8_t MultiIO::shiftRead(){
	readValue = 0;
	digitalWriteDirect(pload_SH, LOW);
	digitalWriteDirect(pload_SH, HIGH);

	for(i=0; i<inPinsCount; i++){
		readValue |= (digitalReadDirect(data_QH) << ((inPinsCount-1)-i));
		digitalWriteDirect(clock_clk, HIGH);
		digitalWriteDirect(clock_clk, LOW);
	}
	return readValue;
}
// Passives Lesen von bestimmte Pin. Es werden alte Registerdaten genutzt ohne shiftRead() zu aufrufen.
bool MultiIO::readPassiv(uint8_t pin){
	return bitRead(readValue, pin);
}
// Lesen von bestimmte Pin. Bei jedem Aufruf wird shiftRead(); aufgerufen um aktuellste Zustand zu bekommen.
bool MultiIO::read(uint8_t pin){
	shiftRead();
	return readPassiv(pin);
}

// PulseIn
unsigned long MultiIO::pulseIn(uint8_t pin, uint8_t state, unsigned long timeout){

	// TimeOut
	unsigned long numloops = 0;
	unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

	// wait for any previous pulse to end
	while(this->read(pin) == state)
		if (numloops++ == maxloops)
			return 0;

	unsigned long width = 0;
	// wait for the pulse to start
	while (this->read(pin) != state)
		if (numloops++ == maxloops)
			return 0;

	// wait for the pulse to stop
	while (this->read(pin) == state) {
		if (numloops++ == maxloops)
			return 0;
		width++;
	}

	return clockCyclesToMicroseconds(width * 21 + 16);

	/*
	//check to see if the pin is already in the value state, if so, the pulse has already begun and the function needs to wait for the next one to "catch" the leading edge.
	//while(this->read(pin) == state);
	// wait for pin state
	while (this->read(pin) != state);

	unsigned long start = micros();

	// wait for pin to change state
	while (this->read(pin) == state);

	unsigned long end = micros();

	// return time in micro seconds
	return (end - start);
	*/
}

// Hollt Abstand bis zum Hinderniss in mm
unsigned long MultiIO::getDdistance(uint8_t trig, uint8_t echo){
	  this->write(trig, LOW);
	  //delayMicroseconds(2);
	  this->write(trig, HIGH);
	  //delayMicroseconds(10);
	  delayMicroseconds(5);
	  this->write(trig, LOW);

	  return this->pulseIn(echo, HIGH);
}
// Ersatz von Standartfunktion (Funktioniert schneller als Stand)
void MultiIO::digitalWriteDirect(uint8_t pin, uint8_t val){
	//digitalWrite(pin, val);
	if(!!val){
		if(pin<=7)
			PORTD |= (1<<PORT_ARRAY[pin]);
		else
			PORTB |= (1<<PORT_ARRAY[pin]);
	}else{
		if(pin<=7)
			PORTD &= ~(1<<PORT_ARRAY[pin]);
		else
			PORTB &= ~(1<<PORT_ARRAY[pin]);
	}

}

// Ersatz von Standartfunktion (Funktioniert schneller als Stand)
uint8_t MultiIO::digitalReadDirect(uint8_t pin){
	//return digitalRead(pin);
	if(pin<=7){
		return !!(PIND & ( 1 << PORT_ARRAY[pin] ));
	}else{
		return !!(PINB & ( 1 << PORT_ARRAY[pin] ));
	}
}

#endif // #ifndef MULTIIO_CPP_

 

maksim
Offline
Зарегистрирован: 12.02.2012

А вот и зря вы не используете SPI, с его помощью вы одновременно сможете читать из 165 регистра и писать в 595. Сделайте тестилку, которая выводит время обработки ваших init595(); и init165();, объявите переменную, перед этими функциями присвойте ее значение micros() и после этих функций выведите разницу текущего значения micros() и переменной. Так вы сможете увидеть за какое время выполняются выши функции и определить точность пульсИн.

ich
Offline
Зарегистрирован: 10.06.2012

Вы возможно не поняли сути функций init595(); и init165(); они ведь просто устанавливают ардуиновские пины для управления регистрами и на pulseIn обсалютно не как не влияют так как они один раз вызываются в void setup().

К стати, с помощью моей MultiIO возможно так же однавременно писать и читать 595 / 165. Просто я пока этим не где не пользуюсь, по этому эти функции и не имплементированны.

А вот на счёт замерения времени, это да, надо с этим по эксперементировать.

maksim
Offline
Зарегистрирован: 12.02.2012

Да имел ввиду те функции, которые записывают/читают в/из регистров.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Аппаратный SPI работает на частоте 4МГц - Вам никогда его не догнать никаким прямым управлением портами. А вот с SPI без библиотеки (через те же порты) работать можно. 

maksim
Offline
Зарегистрирован: 12.02.2012

Не копался в регистрах SPI у мег, поэтому не знаю можно ли задавать тактирование программно, но у тинек на USI в режиме SPI (у USI тактирование линии CLK задается программно, по прерыванию счетчика или внешним источником) можно разогнать до половины частоты МК! То есть при частоте 16 МГц можно разогнать SPI до 8 МГц. Вот этот способ:

byte SPI_transfer(byte data){
//  USISIE USIOIE USIWM1 USIWM0 USICS1 USICS0 USICLK USITC                
//    0      0       0      1      0      0      0     1
//    0      0       0      1      0      0      1     1
  USIDR = data;    
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  USICR = 0b00010001;
  USICR = 0b00010011;
  return (USIDR);
}

Так что если и у мег можно тактирование задать программно, то тоже можно разогнать до такой частоты.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

  

Судя по всему, 8Мгц можно получить и без извратов:

SPI CONTROL REGISTER (SPCR):

Биты 1, 0 – SPR1, SPR0: SPI Clock Rate Select 1 and 0 Соместно с битом SPI2x в регистре SPSR задают скорость передачи данных Биты 1, 0 – SPR1, SPR0: SPI Clock Rate Select 1 and 0 Соместно с битом SPI2x в регистре SPSR задают скорость передачи данных

 

SPI2X SPR1 SPR0 Частота SCK
0 0 0 fosc /4
0 0 1 fosc /16
0 1 0 fosc /64
0 1 1 fosc /128
1 0 0 fosc /2
1 0 1 fosc /8
1 1 0 fosc /32
1 1 1 fosc /64

 SPI STATUS REGISTER (SPSR).

 Полный текст статьи

 

ich
Offline
Зарегистрирован: 10.06.2012

 

Блин во это статейка, ажно страшно стало. Надо будит почитать и по вникать, я так близко с железом ещё не работал и по этому мне это всё, на данный момент, сложновато кажется. Но как говориться: "глаза бояться а руки делают".

Может кто подскажет статейки где хорошо описанно SPI с примерами. Желательно для чайников. Не хочется рыться в куче гоглевских предложений, а хочеться просто прочитать статью и понять. По этому и клянчу нагло готовую статью.

Только не смейтесь: "Если я всётаки разгоню 595 и 165 до 4 или даже до 8МГц, будет тогда работать pulseIn ?".

maksim
Offline
Зарегистрирован: 12.02.2012

 Инфы полно по SPI.
8'000'000/8 = 1'000'000 то есть примерно одна - две микросекунды на получение/отправку одного байта.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

 Вот, здесь попроще - без ассемблера и без прерываний :) Можно еще вспомнить, что интерфейс при передаче байта еще и принимает байт, поэтому SS можно для обоих регистров обЪединить(буду большой твёрдый знак теперь писать - чтобы жирность не выскакивала :)

maksim
Offline
Зарегистрирован: 12.02.2012

Не надо даже копаться в регистрах: arduino.cc/en/Reference/SPI 

Где то здесь на форуме выяснилось, что 595 и 165 принимают и отдают биты по разным фронтам сигнала тактирования, что как раз вам и нужно. По поводу SS, если SS у 165 регистрирует состояние ног (срабатывает защелка) по переднему фронту, а у 595 выводятся значения (срабатывает защелка) по заднему (что и наблюдается у 595), то линия SS может и должна быть общая.

Пример:

#include <SPI.h>
#define SS 2

byte data165, data595;

void setup() {
  SPI.setBitOrder(MSBFIRST); // тут как удовней заполнять байт
  SPI.setDataMode(SPI_MODE0); // здесь не уверен по поводу режима
  SPI.setClockDivider(SPI_CLOCK_DIV2); // Fcpu/2
  SPI.begin();
  DDRB |= 1<<SS; //  pinMode(SS, OUTPUT);  
  PORTB |= 1<<SS; // digitalWrite(SS, HIGH); 
}

void loop(){
  PORTB &= ~(1<<SS); 
  data165 = SPI.transfer(data595);
  PORTB |= 1<<SS;
}

 И вот вам 8 Мгц счастья )

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Красиво.

Вывод - не ленитесь читать библиотечные хедеры - оттуда можно почерпнуть информации больше, чем в описаниях из интернета :) 

maksim
Offline
Зарегистрирован: 12.02.2012

Лишь бы на такой частоте смогли работать регистры... но думаю даташит ответит на этот вопрос.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Регистры - могут. Они, кажется, до 30МГц могут. 

ich
Offline
Зарегистрирован: 10.06.2012

На сколько мне известно, 74HC165 работает до 56MHz а 74HC595 до 100MHz. В любом случае хватит.

Меня интересует вот такое вот высказывание из статьи. В самом конце.

Цитата:

Интерфейс SPI может быть реализован программно на любых ножках МК, но лучше пользоваться встроенным в МК

Как мне эти "любые" пины настроить?

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

"Любые" пины настраиваются точно так же, как и "нелюбые" Но дергать такты, принимать биты и передавать биты нужно самостоятельно, "ручками" :) А "железный" - просто кинул байт в регистр, байт сам передается. 

ich
Offline
Зарегистрирован: 10.06.2012

Понятно, так я и предпологал. Буду юзать "железный", зачем себе жизнь усложнять. :-D

На выходных попробую всё это дело реализовть, в буднии всё руки не доходят. Да и после 8 часов пяленья на монитор на работе охото дома отдохнуть ;-)

 

leshak
Offline
Зарегистрирован: 29.09.2011

 Просто погуглите Arduino SPI software library не вы же первый столкнулись с такой задачей :)

maksim
Offline
Зарегистрирован: 12.02.2012

И софтовый SPI будет медленнее аппаратного.

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:
И софтовый SPI будет медленнее аппаратного.

Это к гадалке не ходи :)

Из того что я видел, даже "а вот мы написали новую быструю библиотеку  софтварного SPI он аж 2mHz выжать может".

ich
Offline
Зарегистрирован: 10.06.2012

leshak пишет:

 Просто погуглите Arduino SPI software library не вы же первый столкнулись с такой задачей :)

Да вы что, правдо думаете что я не искал? А нет, не угадали.

leshak пишет:
... софтварного SPI он аж 2mHz выжать может ...

А это случайно не то что я уже писал и выкладывал?

Да этой скоросит не достаточно :-(. Буду пробовать как правельно, через "железо". Ну очень хочется 8MHz ;-)

ich
Offline
Зарегистрирован: 10.06.2012

Пытался разобратся с SPI но вот что-то не получилось. Проблемма в следующем:

Если пользуюсь библиотекой SPI.h, то всё работает именно так как мне надо. А надо так: подключил, в первом посту указанную платку, к ардуине соединив Latch_ST и Pload_SH в месте. Спасибо подсказки AlexFisher. Написал следующиё код:


#include <SPI.h>

int transport(int value) {
  byte result;
  digitalWrite(10,HIGH);
  result = SPI.transfer(value);
  digitalWrite(10,LOW); 
  return result;
}

void setup() {
  Serial.begin(9600);
  SPI.begin(); 
  SPI.setDataMode(SPI_CLOCK_DIV16);
  pinMode (10, OUTPUT);
}

void loop() {
  for (int i = 0; i < 255; i++) {
    Serial.println(transport(i));
    delay(100);
  }
}

Подключил к 595 светодиоды а к 165 кнопки. Заливаю код в ардуину, светодиоды заморгали. Запустил Serial монитор, светодиоды моргают дальше, при нажатии на кнопки на мониторе появляются соотвецтвующие цифры.

Этого я и хотел добиться, но только написав собственную SPI передачу.

Для написания кода, пользовался следующей информацией:

samou4ka.net/page/interfejs-spi-mikrokontrollerov-avr

code.google.com/p/arduino/source/browse/trunk/libraries/SPI/

www.arduino.cc/en/Tutorial/SPIEEPROM

Вот что получилось:

// http://samou4ka.net/page/interfejs-spi-mikrokontrollerov-avr
// http://code.google.com/p/arduino/source/browse/trunk/libraries/SPI/?r=1066
// http://www.arduino.cc/en/Tutorial/SPIEEPROM
/*
  * CS - to digital pin 10  (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
 */

// Настраиваем мк на рпботу через SPI
void initSPI(){
  DDRB = (1<<3)|(1<<5); //настраиваем на выход
  SPCR = (1<<6)|(1<<4)|(1<<0); //вкл SPI, ведущий, частота fck/16
}
// Передаём данные и одновременно получаем
byte transport(byte in){
  digitalWrite(10,HIGH);
  SPDR = in;
  while(!(SPSR & (1<<7)));
  digitalWrite(10,LOW);
  return SPDR;
}

void setup(){
  Serial.begin(9600);
  initSPI();
  pinMode(10, OUTPUT);
}

void loop(){
  for(byte i = 0; i<255; i++){
    Serial.println(transport(i));
    delay(100);
  }
}

Но к сожелению это не работает так как мне надо. Заливаю код в ардуину, диоды замигали. Пока все как надо. Стоит открыть Serial монитор, всё, диоды останавливаются и ничего не происходит. Так же ни какой реакции на нажатие кнопок. Закрываю монитор, диоды начинают моргать.

В чём дело, почиму программа останавливается? Что я сделал не так, что упустил?

Подскажите пожалуйста.

maksim
Offline
Зарегистрирован: 12.02.2012

Вот функции из библиотеки SPI

void SPIClass::begin() {

  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SS, OUTPUT);
  
  digitalWrite(SCK, LOW);
  digitalWrite(MOSI, LOW);
  digitalWrite(SS, HIGH);

  SPCR |= _BV(MSTR);
  SPCR |= _BV(SPE);
}

void SPIClass::end() {
  SPCR &= ~_BV(SPE);
}

void SPIClass::setBitOrder(uint8_t bitOrder)
{
  if(bitOrder == LSBFIRST) {
    SPCR |= _BV(DORD);
  } else {
    SPCR &= ~(_BV(DORD));
  }
}

void SPIClass::setDataMode(uint8_t mode)
{
  SPCR = (SPCR & ~SPI_MODE_MASK) | mode;
}

void SPIClass::setClockDivider(uint8_t rate)
{
  SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK);
  SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK);
}

byte SPIClass::transfer(byte _data) {
  SPDR = _data;
  while (!(SPSR & _BV(SPIF)))
    ;
  return SPDR;
}

void SPIClass::attachInterrupt() {
  SPCR |= _BV(SPIE);
}

void SPIClass::detachInterrupt() {
  SPCR &= ~_BV(SPIE);
}
#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06
//#define SPI_CLOCK_DIV64 0x07

#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define SPI_MODE2 0x08
#define SPI_MODE3 0x0C

#define SPI_MODE_MASK 0x0C  // CPOL = bit 3, CPHA = bit 2 on SPCR
#define SPI_CLOCK_MASK 0x03  // SPR1 = bit 1, SPR0 = bit 0 on SPCR
#define SPI_2XCLOCK_MASK 0x01  // SPI2X = bit 0 on SPSR

_BV - это тоже самое что и 1<< . Перепроверьте, что бы нумерация битов соответствовала названию битов и при настройке скорости еще что то меняют в регистре SPSR.

ich
Offline
Зарегистрирован: 10.06.2012

Спасибо, заработало.


/*
  * CS  - to digital pin 10 (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
  * SDO - to digital pin 12 (MISO pin)
 */
// Pin's
#define P_SS 2
#define P_MOSI 3
#define P_MISO 4
#define P_SCK 5

// Bit's
#define B_SPE 6
#define B_MSTR 4
#define B_SPI2X 0

// Настраиваем мк на рпботу через SPI
void initSPI(){
  //настраиваем на выход
  DDRB |= _BV(P_MOSI); 
  DDRB |= _BV(P_SCK);
  DDRB |= _BV(P_SS);
  // P_MISO по умолчанию на входе

  SPCR = (1<<B_SPE)|(1<<B_MSTR);  //вкл SPI, ведущий,

  SPSR |= _BV(B_SPI2X); // частота fck/2 = 16/2 = 8MHz
}

// Передаём данные и одновременно получаем
byte transport(byte in){
  PORTB |= _BV(P_SS); // HIGH
  SPDR = in;
  while(!(SPSR & _BV(SPIF)));
  PORTB &= ~_BV(P_SS); // LOW
  return SPDR;
}

void setup(){
  Serial.begin(9600);
  initSPI();
}

void loop(){
  for(byte i = 0; i<255; i++){
    Serial.println(transport(i));
    delay(100);
  }
}

 

Теперь буду дальше ковырять, подключу к MultiIO. Попробую добить pulseIn.

 

ich
Offline
Зарегистрирован: 10.06.2012

Ну вот, проблемы не заставили себя долго ждать.

Не пойму в чём дело. Если управляю Servo с помощью ардуиновских digitalWrite то всё работает как надо. Если пытаюсь управлять Servo с помощью (мной написаного) SPI то Моторчик потихоньку поворачивается на 180° (без разници сколько ° я устанавливаю, один фиг крутится к 180) и его можно рукой двигать. Серва конечно сопративляется но слабо.

Вот весь код:

/*
  * CS  - to digital pin 10 (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * CLK - to digital pin 13 (SCK pin)
  * SDO - to digital pin 12 (MISO pin)
 */
// Pin's
#define P_SS 2    // 10
#define P_MOSI 3  // 11
#define P_MISO 4  // 12
#define P_SCK 5   // 13

// Bit's
#define B_SPE 6
#define B_MSTR 4
#define B_SPI2X 0

#define SERVO_MIN_PULSE 350
#define SERVO_MAX_PULSE 2150
#define SERVO_REFRESH 20

byte outValue = 0;
byte inValue  = 0;
boolean value = HIGH;

// Настраиваем мк на рпботу через SPI
void initSPI(){
  //настраиваем на выход
  DDRB |= _BV(P_MOSI); 
  DDRB |= _BV(P_SCK);
  DDRB |= _BV(P_SS);
  // P_MISO по умолчанию на входе

  SPCR = (1<<B_SPE)|(1<<B_MSTR);  //вкл SPI, ведущий,

  SPSR |= _BV(B_SPI2X); // частота fck/2 = 16/2 = 8MHz
}

// Передаём данные и одновременно получаем
byte transport(byte in){
  PORTB |= _BV(P_SS); // HIGH
  SPDR = in;
  while(!(SPSR & _BV(SPIF)));
  PORTB &= ~_BV(P_SS); // LOW
  return SPDR;
}
// Пишим в конкретный пин по SPI в 595
void writeMIO(byte pin, boolean state){
  if(state)
    outValue |= _BV(pin);
  else
    outValue &= ~_BV(pin);
  inValue = transport(outValue);
}
// Читаем по SPI конретный пин с 165
boolean readMIO(byte pin){
  inValue = transport(outValue);
  return bitRead(inValue, pin);
}
// Управляет сервои через Arduino
void writeToServo(uint8_t pos){
  for(int i=0; i<20; i++){
    digitalWrite(2, HIGH);
    delayMicroseconds(map(pos,0,180,SERVO_MIN_PULSE,SERVO_MAX_PULSE));
    digitalWrite(2, LOW);
    delay(SERVO_REFRESH);
  }
}
// Управляет сервой через SPI
void writeToServoSPI(uint8_t pos){
  for(int i=0; i<20; i++){
    writeMIO(7, HIGH);
    delayMicroseconds(map(pos,0,180,SERVO_MIN_PULSE,SERVO_MAX_PULSE));
    writeMIO(7, LOW);
    delay(SERVO_REFRESH);
  }
}

void setup(){
  Serial.begin(9600);
  pinMode(2,OUTPUT);
  initSPI();
}

void loop(){
  // Пишим с помощью SPI writeMIO(...)
  writeToServoSPI(90);
  // Пишим через Arduino digitalWrite(...)
  writeToServo(90);
  // Моргаем через SPI 8-ми диодами
  for(byte i=0; i<32; i++){
    transport(i);
    delay(10);
  }
}

обе функции (writeToServo и writeToServoSPI) не используют не каких сторонних библиотек.

Мой SPI прекрасно справляется с зажиганием светодиодов на 595 и считыванием состояние каждого пина с 165. Понять не могу почему серва на слушается через SPI.

Пожалйста глянте на код зорким профессиональным взгдядом. Очень уж хочится победить эту проблемку.

Да, вот ещё парачка цифр которые возможно помогут в решении проблемы.

Эти цифры показывают с какой скоростью работают функции (digitalWrite(0,HIGH), digitalRead(1), writeMIO(7,HIGH), readMIO(7))

Arduino (digitalWrite(0,HIGH), digitalRead(1)) #####################
   
    Read 1 = 8 microsec
    Write 1 = 8 microsec

    Read 1000 = 4152 microsec
    Write 1000 = 4300 microsec

    Read 1000000 = 4652856 microsec
    Write 1000000 = 4778652 microsec

SPI 8-MHz (writeMIO(7,HIGH), readMIO(7)) ######################

    Read 1 = 4 microsec
    Write 1 = 4 microsec

    Read 1000 = 2264 microsec
    Write 1000 = 4172 microsec

    Read 1000000 = 2639176 microsec
    Write 1000000 = 4652356 microsec

 P.S.: Не хочу пользоваться ни какими сторонними библиотеками. Хотелось-бы запустить этот код.

 

 

maksim
Offline
Зарегистрирован: 12.02.2012

 Да, странно, вроде все нормально, вам бы осцилографом посмотреть, что у вас с регистра на серву идет.

Кстати, это вам в копилку - bitRead можно заменить

boolean readMIO(byte pin){
  inValue = transport(outValue);
  if(inValue & _BV(pin)) 
     return 1; 
  else 
     return 0;
}

 

maksim
Offline
Зарегистрирован: 12.02.2012

 А если так:

void loop(){
  // Пишим с помощью SPI writeMIO(...)
  writeToServoSPI(90);
  /* Пишим через Arduino digitalWrite(...)
  writeToServo(90);
  // Моргаем через SPI 8-ми диодами
  for(byte i=0; i<32; i++){
    transport(i);
    delay(10);
  }*/
}

 у вас же мигание занимает 320 миллисекунд - серва в это время "отключена"

Вам стоит задуматься о постоянном обмене данными с регистрами - то есть в прерывании по таймеру выполнять функцмю transport().

ich
Offline
Зарегистрирован: 10.06.2012

 

К сожалению, я не обладаю осцилоскопом. :-(

maksim пишет:

А если так:

...

Тот же эфект. Может это из за скорости? Хотя как то тупо, я с разными MIN и MAX пробывал один фиг вертится к 180°.

maksim пишет:

Вам стоит задуматься о постоянном обмене данными с регистрами - то есть в прерывании по таймеру выполнять функцмю transport().

а зачем? Я думаю достаточно вызвать её по нужде!

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Все просто. Серва не управляется отдельными импульсами, фактически, серва управляется скважностью (длиной импульса при постоянной частоте следования импульсов). Так что нужно действительно делать постоянный обмен. Точнее, нужно думать, как вывести на SPI ШИМ, это аналогичная проблемма.

Библиотека адуино использует прерывания от таймеров (в случае UNO - только от таймера 1) . Период следования импульсов 20 мс

ich
Offline
Зарегистрирован: 10.06.2012

AlexFisher пишет:

Все просто. Серва не управляется отдельными импульсами, фактически, серва управляется скважностью (длиной импульса при постоянной частоте следования импульсов).

да. Но если я вызываю только вот это:

void loop(){
  // Пишим с помощью SPI writeMIO(...)
  writeToServoSPI(90);
}

зачем тут прерывания? У меня SERVO_REFRESH равен именно 20 мс.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

И что с того? Она же тупо прибавляется к длине импульса! Вы еще и выполнение программы останавливаете на полсекунды. И позицию она после установки все равно держать не станет, потому что импульсы ей нужно давать постоянно, то есть в фоне, а это делается прерываниями обычно.

Исправте, хотя бы функцию:

// Управляет сервой через SP

#define SERVO_REFRESH 20000

void writeToServoSPI(uint8_t pos){
  int imp=map(pos,0,180,SERVO_MIN_PULSE,SERVO_MAX_PULSE);
  // именно так, ибо нехорошо считать одно и то же 20 раз :)
  for(int i=0; i<20; i++){
    writeMIO(7, HIGH);
    delayMicroseconds(imp);
    writeMIO(7, LOW);
    delayMicroseconds(SERVO_REFRESH-imp);
  }
}

 

ich
Offline
Зарегистрирован: 10.06.2012

Да это понятно что не будет держать. Пока стоит задача только повернуть на нужный ° не удерживая. По этому и цикл с 20 оборотами, чтобы хватало импульсов от 0 до 180 повернуть.

 

AlexFisher пишет:

именно так, ибо нехорошо считать одно и то же 20 раз :)

Согласен, не доглядел. Вот это тоже интересно "delayMicroseconds(SERVO_REFRESH-imp);" сегодня попробую, отпешусь.

ich
Offline
Зарегистрирован: 10.06.2012

Как и претпологал, проблема изменениями не решилась. Но вот работать стало быстрей. Спасибо maksim #37 , AlexFisher #42

Надо гдето искать осцилограф, смотреть что за сигнал я посылаю на серву.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Советую расковырять библиотеку Servo и поменять там весь вывод на свой. Там все управление идет через digitalWrite, но работает по прерыванию. 

ich
Offline
Зарегистрирован: 10.06.2012

Я уже это попробывал, вот по этой статье.

К сожелению результат тотже. Куда бы не крутил серву, она один фиг по тихоньку подползает к 180° и гудит так-же возможно сдвинуть рукой с места.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Если в обычном варианте работает нормально, то остается только смотреть осциллографом сигналы и анализировать.

Но я бы на Вашем месте все же попробовал поковыряться в библиотеке Серво. 

ich
Offline
Зарегистрирован: 10.06.2012

AlexFisher пишет:

Но я бы на Вашем месте все же попробовал поковыряться в библиотеке Серво. 

Я уже ковырялся. До того как я перешел на SPI, с Servo всё отлично работало на старом коде там я обходил стандартные digitalRead/digitalWtite и юзал прямое управление портами Arduino для быстрого управления 595 и 165.

Но вот когда перестроил в старом коде shiftWrite на  transport, начался гон. Т.е. проблема в передачи информации.

Или что Вы имели в виду говоря: "поковыряться в библиотеке Серво"?

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Я имел в виду в функции static inline void handle_interrupts() заменить digitalWrite() на связку setBit(buf_var, x); transport(buf_var); и clearBit(buf_var, x); transport(buf_var);

По идее, должно работать, SPI же быстро выплюнет данные. 

ich
Offline
Зарегистрирован: 10.06.2012

Именно это я и делал:

...

  Channel[timer]++;    // increment to the next channel
  if( SERVO_INDEX(timer,Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
    *OCRnA = *TCNTn + SERVO(timer,Channel[timer]).ticks;
    if(SERVO(timer,Channel[timer]).Pin.isActive == true)     // check if activated
    {
      servo_t &servo = SERVO(timer,Channel[timer]);
      // Заменил на свою функцию
      servo.multi_driver->write(servo.Pin.nbr,HIGH); // Тут я пишу через мой MultiIO
    }
  }
  else

...

Не помогло. Надо осцилографировать! Может кто подскажет програмку для компа, чтобы симулировать осцилограф для ардуины?

Эта почемуто не рааботает.