Автор Тема: Исходники, алгоритмы.  (Прочитано 9259 раз)

0 Пользователей и 1 Гость просматривают эту тему.

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Исходники, алгоритмы.
« : 15 Апрель 2011, 15:09:00 »
Выкладываем здесь различные примеры исходников.

Для начала очень компактное целочисленное вычисление квадратного корня.
unsigned short isqrt(unsigned long x)
{
     unsigned short y = 0;
     unsigned long  z = 1;
     while(x >= z)
    {
         x -= z;
         z += 2;
         y++;
    };
    return y;
}
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Программный ведомый шины I2C на ассемблере
« Ответ #1 : 15 Апрель 2011, 15:55:32 »
Программная реализация ведомого шины I2C.
Так как делалось для конкретной задачи эмуляции LNBP21, реализована простейшая отработка только двух байтов: байт адреса и управляющий байт. Для полноценной реализации протокола I2C требуется доработка. Разобравшимся в исходнике, думаю, это будет несложно.
;Эмулятор шины I2C LNBP21
;Отрабатывает вкл-выкл питания конвертера PINB.3
;а также переключение поляризации PINB.4

.include "tn13def.inc" ;ATtiny13

;Hfuse FF
;Lfuse 7A

.def data      = r23
.def count_bit = r24
.def count_byte= r25

.equ SCL = 1     ;опрделяем пины
.equ SDA = 0     ;шины I2C

.equ ON_OFF = 3   ;определяем выходные
.equ H_V    = 4   ;пины

.equ DEVICE =$10 ;определяем адрес устройства

;***** BEGIN *****
ldi data,(1<<ON_OFF)|(1<<H_V)
out DDRB,data  ;конфигурируем порт на выход

WAIT_START:
ldi count_byte,2

WAIT_SCL:
sbis PINB,SCL  ;ждем когда клок будет 1
rjmp PC-1

sbis PINB,SDA  ;ждем когда data будет 1
rjmp PC-1

sbic PINB,SDA  ;ждем когда data будет 0
rjmp PC-1

sbis PINB,SCL  ;если клок не 1,
rjmp WAIT_SCL  ;это не START, идем ждать клок 1

sbic PINB,SCL  ;ждем когда клок будет 0
rjmp PC-1

;=======принимаем байт ===================
LOOP_st:
ldi count_bit, 8

LOOP:
sbis PINB,SCL  ;ждем когда клок будет 1, при этом ведущий устанавливает бит для передачи
rjmp PC-1

lsl data       ;сдвигаем регистр data на один разряд влево
sbic PINB,SDA  ;проверяем линию данных
ori  data,1    ;если единица, пишем её в data

sbic PINB,SCL  ;ждем когда клок будет 0
rjmp PC-1

dec count_bit   ;уменьшаем счетчик разрядов на 1
brne LOOP      

dec count_byte  ;уменьшаем счетчик байтов на 1
brne ADR       ;если это был 1-й байт, идем на проверку адреса

;-------устанавливаем выходные сигналы-------------
;-------в зависимости от второго принятого байта---
out PORTB,count_bit  ;обнуляем выходы

sbrs data, 3
sbi PORTB,H_V

sbrc data, 2
sbi PORTB,ON_OFF

rjmp ACK
;-----------------------------------------

ADR:
cpi  data,DEVICE ;если адрес не наш, выходим на ожидание условия START
brne WAIT_START

ACK:
sbi DDRB, SDA  ;порт на вывод
cbi PORTB,SDA  ;выводим ноль на SDA, тоесть отвечаем АСК

sbis PINB,SCL  ;ждем когда клок будет 1
rjmp PC-1

sbic PINB,SCL  ;ждем когда клок будет 0,при этом ведущий читает наш АСК
rjmp PC-1

cbi DDRB,SDA   ;порт на ввод - отпускаем СДА

tst count_byte  ;проверяем счетчик принятых байт
brne LOOP_st   ;если не 0, идем принимать второй байт

rjmp WAIT_START
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Вычисление синус, косинус и арктангенс. CORDIC
« Ответ #2 : 15 Апрель 2011, 18:43:43 »
Еще один достойный внимания алгоритм.
/*
Алгоритм CORDIC ([CO]ordinate [R]otation [DI]gital [C]alculation)

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

аргументы и результаты умножаются и делятся на 100 соответственно.
*/

/*=========================
синус-косинус
-------------------------
начальные условия:
mod= 0
angle= угол, y=0, x= 6073

результат:
y = sin
x = cos

=========================
арктангенс
-------------------------
начальные условия:
mod= 1
angle= 0, y = y, x = x

результат:
angle = -atan(y/x)
==========================*/


#define  K 16468
#define  KK 6073 // (1/16468)

const int tab_arctg[13] PROGMEM =
    {4500,2657,1404,713,358,179,90,45,22,11,6,3,1,};

struct codr_data{
int angle;
int y;
int x;
char mod;
};

void CORDIC(struct codr_data *cd)
{                    
int y_next;
int x_next;
int arctg;
int *p = tab_arctg;

for(u8 i=0;i<13;i++)
{
arctg = pgm_read_word(p++);

x_next = cd->x >> i;
y_next = cd->y >> i;

if(cd->mod == 0) //sin cos
{
if(cd->angle < 0)
{
x_next = -x_next;
y_next = -y_next;
}
else arctg = -arctg;
}
else
if(cd->mod == 1) //arctg
{
if(cd->y < 0)
{
x_next = -x_next;
y_next = -y_next;
arctg = -arctg;
}
}

cd->y -= x_next;
cd->x += y_next;

cd->angle += arctg;
}
}
/************ пример использования *********************/
void main(void)
{
struct codr_data cd;
int my_cos, my_sin, my_atan, coord_x, coord_y, my_angl;

cd.mod = 0; //sin cos
cd.angle = my_angl;
cd.y = 0;
cd.x = 6073;

CORDIC(&cd);

my_cos = cd.x;
my_sin = cd.y;


cd.mod = 1; //atan
cd.angle = 0;
cd.y = coord_y;
cd.x = coord_x;

CORDIC(&cd);

my_angl = cd.angle;
}
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Изменяем параметр двумя кнопками
« Ответ #3 : 01 Июль 2011, 07:23:00 »
Изменяем двумя кнопками некое значение по кругу от 0 до limit-1 с шагом step. Направление счета изменяем знаком step.
Операторы сравнения не используются.

void Up_Down_button(unsigned char *value, unsigned char limit, signed char step)
{
*value += limit;

if(Button_Down)*value +=step;
      else
if(Button_Up  )*value -=step;

*value %= limit;
}
while(1||!1);
зберігай спокій

lizard66

  • .
  • *
  • Сообщений: 145
(Нет темы)
« Ответ #4 : 27 Октябрь 2011, 22:43:06 »
Всю тригонометрию делал методом степенных рядов Тейлора-Маклонена, поэтому арккосинус был уже на 90% готов, а корень делал по этому алгоритму:

x1 = 1;
x2 = 0;
while (true) {
  x2 = (x1+N/x1)/2;
  if (fabs(x1-x2) < eps) break;
  x1 = x2;
}

для точности два знака после запятой достаточно 4-5 итераций

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Re: ПО для Primuss3-spectrum
« Ответ #5 : 27 Октябрь 2011, 23:53:41 »
Цитата: "lizard66"
а корень делал по этому алгоритму
Цитата: "lizard66"
Сорри, все пишем на асме
:-):
while(1||!1);
зберігай спокій

lizard66

  • .
  • *
  • Сообщений: 145
(Нет темы)
« Ответ #6 : 28 Октябрь 2011, 08:22:12 »
Цитата: "slav0n"
Цитата: "lizard66"
а корень делал по этому алгоритму
Цитата: "lizard66"
Сорри, все пишем на асме
:-):

Алгоритм так наглядней выглядет или на асме тебе былоб понятней? Сорри за синтаксис, "С" владею плохо, мот надо было переменные обьявить и функции?  :-):

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Re: Исходники , алгоритмы.
« Ответ #7 : 28 Октябрь 2011, 09:41:37 »
Цитата: "lizard66"
Алгоритм так наглядней выглядет или на асме тебе былоб понятней? Сорри за синтаксис, "С" владею плохо
Не думаю, что на асме алгоритмы выглядят понятней чем на C. Почти машинный язык, однако.  :-):
Твой способ на "человеческом" языке выглядит примерно так:
unsigned sqrt_cpu_newton(long L)
{
unsigned rslt = (unsigned)L;
long div = L;
if (L <= 0) return 0;
while (l)
{
div = (L / div + div) / 2;
if (rslt > div) rslt = (unsigned)div;
   else return rslt;
}
}
взято отсюда
while(1||!1);
зберігай спокій

lizard66

  • .
  • *
  • Сообщений: 145
(Нет темы)
« Ответ #8 : 28 Октябрь 2011, 09:47:00 »
Цитата: "slav0n"
взято отсюда

Очень познавательная ссылка, спасибо.

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
скользящий сглаживающий алгоритм
« Ответ #9 : 13 Декабрь 2011, 06:04:27 »
Нашел на хабре интересную формулу

output = alpha*input + (1-alpha)*output[i-1]
0<=alpha<=1

Применительно к микроконтроллерам можно  использовать так:

output = input /2 + output[i-1] /2
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
(Нет темы)
« Ответ #10 : 20 Январь 2012, 18:17:57 »
Вот здесь нашел до кучи еще один вариант сглаживания:

output = output[i-1] + (input - output[i-1])*alpha
0<alpha<1

Цитировать
> Научите какой алгоритм (формулу) требуется установить в контроллер
> для реализации инерционного звена 1-го порядка...

Обозначим X - вход звена, Y - выход звена.

Формула для вычисления выхода инерционного звена 1-го порядка (на языке ST):
Y := Y + (X-Y)*K;

Где K - коэффициент, который обычно вычисляется заранее (один раз) по формуле:
K:=T/(t+T);
Где
t - постоянная времени инерционного звена,
T - период расчета (если расчет выполняется в каждом скане контроллера, то берется время скана; если по прерыванию - то тик прерывания и т.д. )

K имеет смысл в пределах от 0 до 1 (не включая границ). При K=0 выход звена "замораживается" (т.е. постоянная времени звена равна бесконечности), при K=1 звено превращается в повторитель (т.е. постоянная времени звена равна нулю).

>..., интегратора
> (здесь вроде все
> понятно),
> реального дифференциала...

Требуется дополнительная переменная X0 для хранения значения входа из предыдущего скана. Расчет:
Y := (X-X0)*K;
X0 := X;

Где K - коэффициент, который обычно вычисляется заранее (один раз) по формуле:
K:=t/T;
Где
t - постоянная времени дифференцирующего звена,
T - период расчета (если расчет выполняется в каждом скане контроллера, то берется время скана; если по прерыванию - то тик прерывания и т.д.)

> ..., скользящего среднего?

Поскольку Виктор Бардичев уже ответил, добавлю только, что реализация скользящего среднего (СС) в контроллере требует работы с массивами и индексной или косвенной адресацией. Преимущества СС по сравнению с инерционным звеном (экспоненциальным фильтром) неочевидны, поэтому на практике я никогда не сталкивался с необходимостью реализовать именно СС.
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Любопытный способ получения мелодий.
http://countercomplex.blogspot.com/2011 ... ne-of.html
http://countercomplex.blogspot.com/2011 ... music.html

А здесь можно самому поиграться - http://wurstcaptures.untergrund.net/music/
while(1||!1);
зберігай спокій

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
(Нет темы)
« Ответ #12 : 22 Март 2012, 15:38:16 »
Попробовал реализовать этот алгоритм на AVR. Код на Си получается простой до безобразия.  :-):
Для этого используем аппаратный 8-битный таймер в режиме Fast PWM.


Вот собственно код для ATtiny13
#include <avr/io.h>

main(void)
{
TCCR0B=(0<<CS02)|(0<<CS01)|(1<<CS00); // clk
TCCR0A=(1<<COM0A1)|(0<<COM0A0)| //pinout 1-0-1
(1<<WGM01)|(1<<WGM00); //Fast PWM

DDRB = (1<<PB0); //sound out Timer/Counter0

long t;

for(;;t++)
{
//  Немного формул взятых из Интернета. Желаемое раскомментировать.
                // OCR0A = t * ((t>>12|t>>8)&63&t>>4);
// OCR0A = (t>>7|t|t>>6)*10+4*(t&t>>13|t>>6);
// OCR0A = t*(42&t>>10);
// OCR0A = t*((42&t>>10)%14);
// OCR0A = (t*3)&t>>8;
// OCR0A = t*9&t>>4|t*5&t>>7|t*3&t/1024;
// OCR0A = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15)));
// OCR0A = t>>6&1?t>>5:-t>>4;
// OCR0A = t&t%255;
// OCR0A = (t>>6|t|t>>(t>>16))*10+((t>>11)&7);
// OCR0A = ((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t>>6);
// OCR0A = (t*5&t>>7)|(t*3&t>>10);
// OCR0A = t*(((t>>9)&10)|((t>>11)&24)^((t>>10)&15&(t>>15)));
// OCR0A = t*(((t>>9)^((t>>9)-1)^1)%13);
// OCR0A = (t>>5)|(t>>4)|((t%42)*(t>>4)|(0x15483113)-(t>>4))/(t>>16)^(t|(t>>4));
// OCR0A = t>>6^t&37|t+(t^t>>11)-t*((t%24?2:6)&t>>11)^t<<1&(t&598?t>>4:t>>10);

// OCR0A = ((t&4096)?((t*(t^t%255)|(t>>4))>>1):(t>>3)|((t&8192)?t<<2:t));
// OCR0A = t*(t>>((t&4096)?((t*t)/4096):(t/4096)))|(t<<(t/256))|(t>>4);
// OCR0A = ((t%42)*(t>>4)|(0x15483113)-(t>>4))/(t>>16)^(t|(t>>4));

// OCR0A = (((((t*((t>>9|t>>13)&15))&255/15)*9)%(1<<7))<<2)%6<<4;

// OCR0A = ((t>>5&t)-(t>>5)+(t>>5&t))+(t*((t>>14)&14));
OCR0A = (t*(t>>12)*64+(t>>1)*(t>>10)*(t>>11)*48)>>(((t>>16)|(t>>17))&1);

// OCR0A = t*t/(t>>12&t>>8)<<7;
// OCR0A = 8*t*t*(t>>(t>>10)%3+15)/(3+(t>>10&(t>>15&3|4)))|t/16;

char d = 200; // переменной d подбираем темп мелодии
while(--d)asm("nop"::);
}
}
while(1||!1);
зберігай спокій

Azumi

  • .
  • *
  • Сообщений: 18
Re: Исходники , алгоритмы.
« Ответ #13 : 23 Март 2012, 14:59:58 »
slav0n, для привязки дисплея к примусу, Вы использовали самодельную библиотеку? Не поделитесь версиеи для кодвижна?
А то мне понравилось такое расположение ножек, чтоб все не на одном порту висело, а на разных, произвольно.

slav0n

  • Администратор
  • .
  • *****
  • Сообщений: 4680
Re: Исходники , алгоритмы.
« Ответ #14 : 23 Март 2012, 15:26:23 »
Цитата: "Azumi"
для привязки дисплея к примусу, Вы использовали самодельную библиотеку? Не поделитесь версиеи для кодвижна?
Для какого конкретно дисплея, символьного или графического? Да,и я пишу в WinAvr. Синтаксис немного отличается от кодвижена.
while(1||!1);
зберігай спокій