Jump to content
pryanic

Обработка Инкрементального Энкодера На Прерываниях.

Recommended Posts

Вообще планирую запихать все действия в обаботчик, ведь если будет другой код, то х.з. в какой момент наступит прерывание...

Share this post


Link to post
Share on other sites

Ну и что с того. Сохранишь в стеке используемые в прерывании регистры, выполнишь прерывание, восстановишь регистры и продолжишь с момента "х.з.+1" (автоматом), как ничего и не было. Для этого прерывания и придуманы.

Share this post


Link to post
Share on other sites

Я о том, что часть вычислений в вашем алгоритме выполняется в основном цикле программы.

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

Share this post


Link to post
Share on other sites

Литиевые батарейки Fanso для систем телеметрии и дистанционного контроля

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

Подробнее

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

Share this post


Link to post
Share on other sites
                     

Приглашаем на вебинар Решения для построения ультразвуковых счетчиков жидкостей и газов на базе MSP430

Компэл совместно с Texas Instruments 23 октября 2019 приглашают на вебинар, посвященный системам-на-кристалле для построения ультразвуковых расходомеров жидкостей и газов на базе ядра MSP430. Вебинар проводит Йоханн Ципперер – эксперт по ультразвуковым технологиям, непосредственно участвовавший в создании данного решения. На вебинаре компания Texas Instruments представит однокристальное решение, позволяющее создавать точные недорогие счетчики жидкостей и газов.

Подробнее...

Немного ошибся, точнее будет так (составлял по Вашему листингу):

post-122868-0-18982000-1355683906_thumb.png

Вот мой листинг: encoder_test3.zip

Edited by pryanic

Share this post


Link to post
Share on other sites

Вечером взгляну. А в железе работает? Посмотрел алгоритм, похоже на истину. В листинге нет выхода из прерывания (reti). В остальном - не вдавался, извини. Туго мысли идут (наверное перед праздником), а тут ты еще все перепахал под свой подчерк. Пробуй в железе, если что - звони, разберемся.

Edited by Геннадий

Share this post


Link to post
Share on other sites

Геннадий, к сожалению в связи с наступающей сессией работа приостановлена. Еще раз посмотрел алгоритм (сверился с Вашим кодом).

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

post-122868-0-07884700-1356288060_thumb.png

Share this post


Link to post
Share on other sites

Надо пробовать. Не попасть бы тебе на состояние колебания вперед/назад на одном месте. О котором вначале говорили.

Share this post


Link to post
Share on other sites

Нашлось наконец-то время заняться. Но по прежнему все не особо весело.

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

В аттаче проект студии.

encoder_test3.zip

Edited by pryanic

Share this post


Link to post
Share on other sites

Антон, как я понял, ты используешь механический энкодер. В таком случае нужно немного изменить последовательность действий. Поскольку существует дребезг контактов, то не нужно опрашивать состояние второго вывода энкодера сразу после тактового перепада на первом выводе. Попробуй так: тактовый перепад -> задержка 20-30 мс -> проверка состояния второго вывода.

Share this post


Link to post
Share on other sites

Алексей,пробовал. С такого алгоритма я начал. Пробовал разные задержки. - работало плохо.

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

Share this post


Link to post
Share on other sites

Геннадий, намек понял. Т.е. щупать несколько раз, пока не перестанет изменяться состояние?

Edited by pryanic

Share this post


Link to post
Share on other sites

Чтобы тут тоже было. Рабочий кусок. Сейчас работает в никитинском регуляторе.

unsigned int scanenc(void)
{
encnew=((PIND&0b00001100)>>2); //читаем состояние ног энкодера
if (encnew==(enccode&0b00000011)) return(code); //если состояние не поменялось, то возвращаем код.
enccode=(enccode<<2)|encnew; //запоминаем последовательно цепочку состояний

//сравниваем получившуюся последовательность с последовательностями для инкремента и декремента
if ((enccode == 0b10000111)&(code>0)) code--;
if ((enccode == 0b01001011)&(code<0x1F)) code++;
return (code);
}

Вызывается по прерываниям таймера антидребезга от обеих ног. Сначала делал тактовый/направление, работало плохо.

Прерывания INT0 и INT1 на обе ноги. По изменению уровня, то есть по обоим фронтам.

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

Share this post


Link to post
Share on other sites

Спасибо, Евгений. Но я на асме пишу.

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

post-122868-0-34421200-1361458579_thumb.gif

И все заработало как часы!!!! Правда задержку пришлось сделать меньше ожидаемой. Вначале была примерно 16 мс (4 МГц, предделитель 256), но при быстром вращении видимо цепляло уже из следующего цикла, установил предделитель на 8 и даже при самом быстром вращении не происходит никаких неожиданностей.

Спасибо всем учавствовавшим.

encoder_test2.zip

Edited by pryanic

Share this post


Link to post
Share on other sites

Спасибо, Евгений. Но я на асме пишу.

Какая разница, главное ведь алгоритм :).

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

И все заработало как часы!!!!

Вот с кондерами у меня тоже по такому принципу нормально работало. А без кондеров либо лишние импульсы, либо приходится делать очень жесткий андидребезг и в итоге на высокой скорости вращения импульсы не считаются. Алгоритм, что я привел выше, замечательно работает без конденсаторов. Лишние детали - не наш метод )))

Edited by GeniusXZ

Share this post


Link to post
Share on other sites

подниму старую тему.

на днях занялся освоением энкодера.

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

1. через внешнее прерывание. канал А заводится на прерывание, например, INT0, а при обработке прерывания опрашивается канал В.

(именно с этого алгоритма и началась эта тема).

2. опрос по таймеру обоих сигналов энкодера.

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

соответственно кажущейся "железобетонности", я начал осваивать по внешнему прерыванию (INT0). в обработке прерывания я сразу же запрещал это прерывание и запускал таймер, которой через некоторое время опять разрешал это внешнее прерывание. прерывание по таймеру отключало этот таймер.

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

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

для МК  я пишу только на ассемблере. тексты на С я даже читать отказываюсь.

даю фрагменты своего текста, определяющие работу с энкодером.

Скрытый текст

.include "m8def.inc"	; ATMega8
; внутренний RC генератор 1 МГц

...

;------ настройка Таймера0 ------
ldi R16, 2			; предделитель = 8, интервал срабатывания Таймера0 2048 мкс
out TCCR0, R16

...

;--- прерывание по Таймеру0 ---
timer0:
push R16				; сохраним регистр, который использует прерывание
in R16, SREG			; сохраним SREG
push R16				; сохраним SREG
; канал A энкодера - порт D0
; канал B энкодера - порт D1
in new_state_enc, PinD
andi new_state_enc, 3

cp old_state_enc, new_state_enc	; если состояние не поменялось, то выходим
breq nothing

cpi new_state_enc, 0	; если состояние поменялось, выбираем подходящий вариант
breq case0
cpi new_state_enc, 1
breq case1
cpi new_state_enc, 2
breq case2
cpi new_state_enc, 3
breq case3
rjmp nothing			; блокировка на всякий случай, хотя до этой команды никогда не должно дойти

case0:
cpi old_state_enc, 1
breq Iref_plus
cpi old_state_enc, 2
breq Iref_minus
rjmp nothing			; блокировка на случай ошибочной комбинации состояний энкодера

case1:
cpi old_state_enc, 3
breq Iref_plus
cpi old_state_enc, 0
breq Iref_minus
rjmp nothing			; блокировка на случай ошибочной комбинации состояний энкодера

case2:
cpi old_state_enc, 0
breq Iref_plus
cpi old_state_enc, 3
breq Iref_minus
rjmp nothing			; блокировка на случай ошибочной комбинации состояний энкодера

case3:
cpi old_state_enc, 2
breq Iref_plus
cpi old_state_enc, 1
breq Iref_minus
rjmp nothing			; блокировка на случай ошибочной комбинации состояний энкодера

Iref_plus:
mov old_state_enc, new_state_enc	; копируем в темп1 новое состояние выводов
inc R14
brne no_0_R14
inc R15
no_0_R14:
rjmp nothing

Iref_minus:
mov old_state_enc, new_state_enc	; копируем в темп1 новое состояние выводов
ldi R16, 1
sub R14, R16
brcc no_R14
sbc R15, R31
no_R14:

nothing:
pop R16					; восстановим SREG
out SREG, R16			; восстановим SREG
pop R16					; восстановим регистр, который использует прерывание
reti

...

;--- это делается в основном цикле программы с интервалом 0,1 секунды ---
;--- обработка результата вращения энкодера ---
ldi R16, par_Iref	; подставим номер параметра "задание тока"
rcall get_parametr	; получим в R21:R20 параметр
add R20, R14
adc R21, R15
cp R20, R31
cpc R21, R31
brpl no_Iref_min
mov R20, R31
mov R21, R31
no_Iref_min:
ldi R16,  low(500)
ldi R17, high(500)
cp R16, R20
cpc R17, R21
brcc no_Iref_max
mov R20, R16
mov R21, R17
no_Iref_max:
ldi R30, spisok_par + (par_Iref<<1)
st Z+, R20
st Z, R21
clr R14				; очищаем регистры счетчика энкодера (приращение к параметру)
clr R15

...

 

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

;    R14, R15    - счетчик энкодера

как можно видеть, признаком направления вращения служит само число, точнее, знак числа в счетчике энкодера. при вращении вправо получается положительное число, а при вращении влево - отрицательное.

поэтому в основном цикле пропадает необходимость в анализе направления, и достаточно к параметру просто прибавить получившееся число энкодера.

после суммирования я делаю проверку на минимум, на ноль (в регистре R31 находится ноль), и проверку на максимум (число 500).

после чего я сохраняю новое значение параметра (или старое, если энкодер не вращали) и обнуляю счетчик энкодера.

 

блин... знаки табуляции разбежались и комментарии встали хаотически...

Share this post


Link to post
Share on other sites

я эту статью уже читал.

на Радиокоте уже дали хорошую подсказку.

счетчик энкодера перевел на регистры R24 и R25, благодаря чему удалось применить команды

adiw R24, 1
и
sbiw R24, 1

что сократило код на десяток байт.

 

Share this post


Link to post
Share on other sites
ISR(INT0_vect)//для atmega328 если появился сигнал на int0  PD2
{
	if (PINB&(1<<7))//то если на PB7 появилась 1 (крутим влево)
	{
		EncData++;
	}
	if (!(PINB&(1<<7)))//если на PB7 0 (крутим вправо)
	{
		EncData--;
	}
}

короче чтоб не срать в других темах буду .... здесь. как я понимаю это что типа так?

только я не понимаю как в нее передать и сохранить EncData.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Similar Content

    • By Вячеслав_НС
      Здравствуйте ! подскажите , какую лучше библиотеку использовать (и где ее взять) для управления i2c atmega8 в atmel studio 7  .  задача - управлять atmega8 по i2c ,  цап  PCF8591T .
      понимаю , в интернете много примеров и библиотек , но хотелось бы пример максимально простой и точно рабочий . т.к. пока что все мои попытки не привели к успеху ... то Atmel Studio 7 при компиляции ругается на несуществующий файл ( к примеру - "stream.h") при использовании библиотеки i2c , то еще какие то грабли .
    • By Антон Плюшкин
      В общем есть небольшая тривиальная задача - сделать свитюльку. Контроллер управляет светодиодиками, цвета меняются, людишки довольны.
      Схема проста: Attiny44a -> 2n3904 x3 -> RGB-светодиод.
      Собрал, протестил, всё норм, но!
      Как только в коде я использую функцию задержки - _delay_ms (util/delay.h) - контроллер повисает!
      #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> int main(void) { // Input/Output Ports initialization // Port A initialization // Function: Bit7=Out Bit6=Out Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In DDRA=(1<<DDA7) | (1<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0); // State: Bit7=0 Bit6=0 Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0); // Port B initialization // Function: Bit3=In Bit2=Out Bit1=In Bit0=In DDRB=(0<<DDB3) | (1<<DDB2) | (0<<DDB1) | (0<<DDB0); // State: Bit3=T Bit2=0 Bit1=T Bit0=T PORTB=(0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0); // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 16000,000 kHz // Mode: Phase correct PWM top=0xFF // OC0A output: Non-Inverted PWM // OC0B output: Non-Inverted PWM // Timer Period: 0,031875 ms // Output Pulse(s): // OC0A Period: 0,031875 ms Width: 0 us // OC0B Period: 0,031875 ms Width: 0 us TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (1<<WGM00); TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00); TCNT0=0x00; OCR0A=0x00; OCR0B=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 16000,000 kHz // Mode: Ph. correct PWM top=0x00FF // OC1A output: Non-Inverted PWM // OC1B output: Disconnected // Noise Canceler: Off // Input Capture on Falling Edge // Timer Period: 0,031875 ms // Output Pulse(s): // OC1A Period: 0,031875 ms Width: 0 us // Timer1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10); TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10); TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; // Timer/Counter 0 Interrupt(s) initialization TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (0<<TOIE0); // Timer/Counter 1 Interrupt(s) initialization TIMSK1=(0<<ICIE1) | (0<<OCIE1B) | (0<<OCIE1A) | (0<<TOIE1); // External Interrupt(s) initialization // INT0: Off // Interrupt on any change on pins PCINT0-7: Off // Interrupt on any change on pins PCINT8-11: Off MCUCR=(0<<ISC01) | (0<<ISC00); GIMSK=(0<<INT0) | (0<<PCIE1) | (0<<PCIE0); // USI initialization // Mode: Disabled // Clock source: Register & Counter=no clk. // USI Counter Overflow Interrupt: Off USICR=(0<<USISIE) | (0<<USIOIE) | (0<<USIWM1) | (0<<USIWM0) | (0<<USICS1) | (0<<USICS0) | (0<<USICLK) | (0<<USITC); // Analog Comparator initialization // Analog Comparator: Off // The Analog Comparator's positive input is // connected to the AIN0 pin // The Analog Comparator's negative input is // connected to the AIN1 pin ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0); ADCSRB=(0<<ACME); // Digital input buffer on AIN0: On // Digital input buffer on AIN1: On DIDR0=(0<<ADC1D) | (0<<ADC2D); // ADC initialization // ADC disabled ADCSRA=(0<<ADEN) | (0<<ADSC) | (0<<ADATE) | (0<<ADIF) | (0<<ADIE) | (0<<ADPS2) | (0<<ADPS1) | (0<<ADPS0); unsigned char VL_OCR0A = 0, VL_OCR0B = 0, VL_OCR1A = 0; OCR0A = 0; OCR0B = 0; OCR1A = 0; while(1) { _delay_ms( 100 ); VL_OCR0A = VL_OCR0A + 1; VL_OCR0B = VL_OCR0B + 1; VL_OCR1A = VL_OCR1A + 1; if( VL_OCR0A >= 250 ){ VL_OCR0A = 0; } if( VL_OCR0B >= 250 ){ VL_OCR0B = 0; } if( VL_OCR1A >= 250 ){ VL_OCR1A = 0; } OCR0A = VL_OCR0A; OCR0B = VL_OCR0B; OCR1A = VL_OCR1A; } } Т.е. если указать задержку в начале цикла, то светодиод не светится вообще.
      Если задержку убрать - светодиод немного подсвечивает всеми цветами.
      while(1) { _delay_ms( 100 ); // <--- ЗАДЕРЖКА --- VL_OCR0A = VL_OCR0A + 1; VL_OCR0B = VL_OCR0B + 1; VL_OCR1A = VL_OCR1A + 1; if( VL_OCR0A >= 250 ){ VL_OCR0A = 0; } if( VL_OCR0B >= 250 ){ VL_OCR0B = 0; } if( VL_OCR1A >= 250 ){ VL_OCR1A = 0; } OCR0A = VL_OCR0A; OCR0B = VL_OCR0B; OCR1A = VL_OCR1A; } Где я накосячил?
      З.Ы.: Замечаний по поводу оптимизации кода, излишних переменных и п.р. прошу не писать - изощряюсь как могу ибо не пойму почему не работает
    • By LegionKC
      Добрый день.
      Экспериментирую с платой TP4056 и MK ATmega8, конкретнее - пытаюсь собрать простенькое зарядное устройство с выводом параметров при зарядке/разрядке на АЦП МК. Для измерения тока нашел ACS712. Думаю, что получится выводить ток при зарядке (т.е. когда идет питание на TP4056 и нагрузка отключена) и при разрядке (питание выключено, нагрузка подключена). Нагрузка - резистор. Возникла проблема с измерением напряжения на АКБ. Вернее, проблема с отсутствием идей как это сделать. Может какую-нибудь схему делителя напряжения нужно сделать? Прошу помочь советом. Схему из пэинта прилагаю.
      Спасибо.
       

    • By Cheshire Cat
      Здравствуйте, только начинаю изучать программирование мк. Я уже задавал этот вопрос в моих прошлых схемах мне отвечали что-то типа "любой стабилизрованый источник постоянного напряжения", или кидали схему такого источника. Можно назвать мне конкретное устройство? В общем то что я смогу забить в гугл и приписать купить. 

      Я уже больше двух месяцев жду пока мне придут с aliexpress детали для маленького лабараторного блока, но этот вариант может разбиться о кривизну моих рук.  Хочу просто купить такой источник и наконец заняться непосредственно МК. 
    • By Cheshire Cat
      Здравствуйте, только начал изучать программирование микроконтроллеров. В учебнике есть схема, но она не учитывает питание и землю. Скажите правильно ли я её дополнил.
      И еще дополнительный вопрос. Что можно использовать как источник постоянного питания на 5В? Из не дорогого и того, что можно купить в Казани. Знаю, что лучший вариант - лабораторный БП, но они дорогие.

  • Сообщения

    • Хосподи, клавиатуры то зачем? Шнурок отчекрыжил да и всё.
    • Именно такое фото очень подчеркивает всю ширину, размер  бездну карих глаз 
    • Или пойти в нормальный магазин промоборудования и накупить 10 ваттных прожекторов, которые, между прочим, имеют исполнение IP65  и действительно рассчитаны на использование на открытой улице, в отличии от бытовых ламп. Стоят не сильно дороже ламп, а при оптовой закупке могут и неплохую скидку оформить.
    • А я считаю,что к месту, т.к. речь шла о принципе построения АС - ГИ.
    • @BAFI а с каких пор 107-ой стал шоттки? Чет я пропустил наверное тот момент второй вопрос - нахрена он там FR? Или двигатель настолько высокочастотный? третий вопрос - неужели у IRFZ44 такой огромный ток затвора, что непременно нужны BD139/140? ну и четвертый - накой хрен это все на 494, когда можно на 5 деталях это же самое сделать на 34063 без потери качества регулирования?
    • Там 3 миллиграмма золота... Потому и стоят дорого http://www.radel.ru/shop/products/view/607361 
    • Читаю тему пока урывками как позволяет время . Пока не видел базовую прошивку как в соседней теме предлагает  "asvetp" от которой можно отталкиваться чтоб заявлять о каких-то хотелках , по этой причине без особых притязаний . Только если два вида настройки автомат и энкодером или кнопками в ручную вперед - назад  с последующей записью кнопкой ( так как скорей всего записанных автоматом пораженок будет море ) и дальнейшей  возможностью переключаться по записанным каналам  . Если что объясните что дает прибавка баса , если по даташиту микрушка ниже 100 Гц толком ни чего не воспроизводит что на наушники очень заметно и прибавка скорее всего вызовет только отвратный бубнеж а не мягкий бархатистый бас (хотя на радиостанциях тоже так низко и глубоко не вещают ), если не так то просветите пожалуйста ? И если только одно не скромное пожелание - возможность включения выключения и рулежки каналами каким нибудь ИК пультом типа как от китайских mp3 "блееров" или классического телевизионного RC-5
  • Покупай!

×
×
  • Create New...