ATtiny85 1001 применение.
- Войдите на сайт для отправки комментариев
Втр, 04/01/2022 - 18:16
Как-то незаслуженно обидели весьма достойный микроконтроллер. Из особенностей наличие умножителя частоты и возможность работы таймера до весьма высоких частот, 100 мегагерц уж точно, что позволяет сделать к примеру частотомер.
Надеюсь в этой теме появятся скетчи ориентированные именно на этот микроконтроллер.
А что, мне он нравится, на нем даччики первой линии оч. хорошо получаются. Всякие уровни, протечки, термостаты и т.д. И жрёть немного.
Жалко, подорожал в 3 раза.
100 мегагерц уж точно
64 максимум
100 мегагерц уж точно
64 максимум
встречал частотомер на 100 мегагерц
А что, мне он нравится, на нем даччики первой линии оч. хорошо получаются. Всякие уровни, протечки, термостаты и т.д. И жрёть немного.
Жалко, подорожал в 3 раза.
я только в начале пути, получил сегодня десяток, залил туда загрузчик от DigiSpark, напрямую с IDE не получилось.
Для заливки загрузчика понадобится bat файл с содержимым:
И прошивка в файле ATtiny85.hex:
Файлы закинуть в каталог с программой IDE, запускать bat от администратора, в качестве программатора ARDUINO AS ISP.
Прошивает почему-то со второго раза, может что с программатором, конденсатор на резет не вешал.
Важно!!!
Установка драйверов возможна только с микроконтроллером в котором зашит только загрузчик, то-есть WINDOWS должна хотя бы раз найти ваше устройство и установить драйвера на выбранный порт USB.
Простой блинк без delay()
// BLINK для ATtiny85 // LED_BUILTIN на пине D1 uint32_t timer; bool flag = true; #define work_time 500 #define period 500 void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN,flag); } void loop() { if (millis() - timer >= (flag ? work_time : period)) { timer = millis(); flag = !flag; digitalWrite(LED_BUILTIN, flag); } }volatile bool leds = true; uint16_t divs = 3; // программный делитель volatile uint16_t clocks; uint16_t tiks = 0; #define work_time 5 #define period 15 ISR (TIMER1_COMPA_vect) { clocks++; if (clocks == divs) { clocks = 0; tiks++; if(tiks >= (leds ? work_time : period)){ tiks = 0; leds = !leds; digitalWrite(PB1,leds); } } } void setup() { DDRB = (1 << PB1); OCR1A = 83; OCR1C = 165; TCCR1 = (1 << PWM1A) | (15 << CS10); // |(1<<COM1A0)2)|(1<<CS11); TIMSK = 1 << OCIE1A; digitalWrite(PB1,leds); } void loop() { }Нахрен загрузчик. Скетч - экспорт бинарного файла и программатор это наше всё. Как оказалось бонусом генериться lst файл с отличным листингом.
Не будем о наших и ваших. Люди хотят как проще.
Не будем о наших и ваших. Люди хотят как проще.
При этом для простоты приходится делать ряд важных шагов и при этом не ошибаться )))
Другое дело, что без программатора, хотя бы элементарного на основе ардуины с этими микроконтроллерами ничего не сделаешь.
Первый программатор для них был несколько диодов и резисторов в сом порт компа.
Первый программатор для них был несколько диодов и резисторов в сом порт компа.
Громова? Если он - видел как Натаха из Липецка его ваяла, на ютубе, сейчас она металлом увлечена )))
Тренажёр для изучения телеграфной азбуки, передаёт в средневолновом диапазоне.
Как пример использования умножителя PLL.
Статья на Хабре.
Схема:

Код программы:
Макетная плата:

Компиляция программы:
Для компиляции используйте ATTiny Core от Spence Konde6.
Во вкладке ATtinyCore выберите опцию ATtiny25/45/85 (No bootloader), ведущую в меню Board.
Убедитесь, что опции выставлены как указано ниже (прочие игнорируйте):
Chip: «ATtiny85»
Clock: «8 MHz (internal)»
Выберите Burn Bootloader для установки фьюз-битов в соответствии с этим вариантом частоты.
Затем загрузите программу, используя ISP (внутрисхемное программирование).
В переводе статьи есть ошибки в расчёте частоты излучения, должно быть:
The carrier frequency is:
тини85 это та же атмега8/88, только с меньшим числом ног. Особенностей минимум. Скетчи в Ардуино ИДЕ. Че там осваивать? :)
Я вот все мечтаю начать колхозить мелкие девайсы на стм8, коих у меня целый пакетик :)... но все времени не хватает...
тини85 это та же атмега8/88, только с меньшим числом ног. Особенностей минимум. Скетчи в Ардуино ИДЕ. Че там осваивать? :)
Я вот все мечтаю начать колхозить мелкие девайсы на стм8, коих у меня целый пакетик :)... но все времени не хватает...
в атмеге 8 есть PLL?
в атмеге 8 есть PLL?
не знаю
в атмеге 8 есть PLL?
не знаю
цимес ATtiny в наличии умножителя и высокоскоростного таймера )))
Реле оттайки (ТИМ-1) на ATtiny85:
// Настройка фьюзов для ATtiny85 // Fuse High Byte // D7 - RSTDISBL = 1 (External Reset enabled, if disabled SPI low voltage programming is impossible) // D6 - DWEN = 1 (debugWire disabled, if enabled SPI low voltage programming is impossible) // D5 - SPIEN = 0 (Serial Programming enabled, if disabled low and high voltage programming is impossible) // D4 - WDTON = 1 (Watchdog Timer is not locked to be reset source, it can be set-up by software) // D3 - EESAVE = 1 (EEPROM is not preserved through Chip Erase) // D2 - BODLEVEL2 = 1 (не программируется) // D1 - BODLEVEL1 = 1 (не программируется) // D0 - BODLEVEL0 = 1 (не программируется) // Fuse Low Byte // D7 - CKDIV8 = 0 (CLKPR = 3 upon start: divide clock by 8) // D6 - CKOUT = 1 (не программируется) // D5 - SUT1 = 1 (не программируется) // D4 - SUT0 = 0 () // D3 - CKSEL3 = 1 (MHz) // D2 - CKSEL2 = 0 (MHz) // D1 - CKSEL1 = 1 (не программируется) // D0 - CKSEL0 = 0 (MHz) #include <avr/io.h> #include <stdlib.h> #include <avr/interrupt.h> #include <util/delay.h> #include <avr/eeprom.h> // не забываем подключить #include <EEPROM.h> #define freezer 0x00 //; freeser mode #define heater 0x01 //; heater mode #define _1min 12 //; 1 min const #define _1hour 60*_1min //; 1 hour const #define _2min 2*_1min //; 2 min const #define _8min 8*_1min //; 8 min const #define _8hour 8*_1hour //; 8 hour const #define ac_tick 15/0.02 //; количество 20-ти миллисекундных тиков за 15 секунд #define xp_tick 8*60*60/15 //; количество 15 секундных тиков в 8-часах работы #define po_tick 10*60/15 //; количество 15 секундных тиков за 10 минут #define ao_tick 40*60/15 //; количество 15 секундных тиков за 40 минут volatile int schetchik = 0; // считает в 15 секундных интервалах volatile int ac_schetchik = 0; bool ac_flag = 0; bool btn_flag = 0; volatile byte work_mode = 0; // 0 - рабочий режим, 1 - активная оттайка, 2 - пассивная оттайка // Прерывания // INT0 обрабатывает нажатие кнопки (настраивать INPUT_PULLUP) // PCINT2 (настраивать INPUT) ISR(INT0_vect) { // наш код обработки нажатия кнопки принудительного оттаивания if (!btn_flag) // еще не было нажатия кнопки { btn_flag = 1; digitalWrite(0, HIGH); // включим реле оттайки schetchik = 0; // сбросим счетчик в ноль work_mode = 1; EEPROM.write(2, work_mode); } } // Обработчик прерывания PCINT0 ISR(PCINT0_vect) { ac_schetchik++; if (ac_schetchik >= ac_tick) { ac_flag = 1; // увеличить счетчик в EEPROM ac_schetchik = 0; } } //Функции (с) Кэп void EEPROM_write(int uiAddress, char ucData) { while (EECR & (1 << EEPE)); /*Ждать завершения предыдущей записи*/ EEAR = uiAddress; /*Проинициализировать регистры*/ EEDR = ucData; EECR |= (1 << EEMPE); /*Установить флаг EEMWE*/ EECR |= (1 << EEPE); /*Начать запись в EEPROM*/ } char EEPROM_read(int uiAddress) { while (EECR & (1 << EEPE)); /*Ждать завершения предыдущей записи*/ EEAR = uiAddress; /* Проинициализировать регистр адреса*/ EECR |= (1 << EERE); /*Выполнить чтение*/ return EEDR; } void EEPROMWriteInt(int p_address, int p_value) // принимает 2 параметра, адресс и число 0...65534 { // забирает 2 ячейки, так что использовать ячейки 0, 2, 4, 6... byte lowByte = ((p_value >> 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM_write(p_address, lowByte); EEPROM_write(p_address + 1, highByte); } int EEPROMReadInt(int p_address) //Считывает число в диапазоне 0...65534 { // использует 2 ячейки, так что считывает ячейки 0, 2, 4, 6... byte lowByte = EEPROM_read(p_address); byte highByte = EEPROM_read(p_address + 1); return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); } void setup() { DDRB = 0b00000001; // pinMode(0, OUTPUT); PORTB &= ~(1 << PB0); // digitalWrite(0, LOW); pinMode(1, INPUT); // D1 PB1 (ATTINY13A - Attiny13) PORTB |= (1 << PB1); // D1 - INPUT_PULLUP pinMode(2, INPUT); // D2 PB2 (ATTINY13A - Attiny13) // Настройка прерываний // Инициализация INT0 и PCINT0 cli(); // Запрещаем прерывания на время изменения WDE и WDP MCUCR = _BV(ISC01) | _BV(ISC00); // FALLING // RISING - 11, LOW - 00,HIGH - 01, FALLING - 10 GIMSK |= (1 << PCIE) | _BV(INT0); // Разрешаем внешние прерывания PCINT0, INT0 PCMSK |= (1 << PB2)/* | (1<<PB1)*/; // Разрешаем по маске прерывания на ноге (PCINT2) sei(); // Разрешаем прерывания глобально: SREG |= (1<<SREG_I) // восстанавливаем значение счетчика и режим работы work_mode = EEPROM.read(2); schetchik = EEPROMReadInt(0); } void loop() { work_mode = EEPROM.read(2); // Раз в 15 секунд пишем счетчик в EEPROM if (ac_flag) { schetchik++; EEPROMWriteInt(0, schetchik); ac_flag = 0; } if (work_mode == 0) { if (schetchik >= xp_tick /*8 часов */) { // включить режим активной оттайки digitalWrite(0, HIGH); work_mode = 1; EEPROM.write(2, work_mode); schetchik = 0; // запущен таймер активной оттайки } } if (work_mode == 1) { if (schetchik >= ao_tick /*8 часов */) { work_mode = 2; EEPROM.write(2, work_mode); schetchik = 0; // запущен таймер пассивной оттайки } } if (work_mode == 1) { if (schetchik >= po_tick /*8 часов */) { btn_flag = 0; work_mode = 0; EEPROM.write(2, work_mode); schetchik = 0; // запущен таймер пассивной оттайки digitalWrite(0, LOW); // переключиться в режим охлаждения } } }Можно ли настроить на вывод отрицательных температур?
#include <TM1637Display.h> #include <OneWire.h> #include <DallasTemperature.h> // Module connection pins (Digital Pins) #define CLK 4 #define DIO 3 #define ONE_WIRE_BUS 2 const uint8_t SEG_ERR[] = { SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, // E SEG_E | SEG_G, // r SEG_E | SEG_G, // r 0, // space }; TM1637Display display(CLK, DIO); OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); void setup() { display.setBrightness(0x02); sensors.begin(); if (sensors.getDS18Count() == 0) display.setSegments(SEG_ERR); else sensors.setResolution(12); } void loop() { if (sensors.getDS18Count() != 0) { sensors.requestTemperatures(); double temp = sensors.getTempCByIndex(0); display.showNumberDecEx(temp * 100, 0b01000000, false); } delay(1000); }Готов поспорить, до 1001 применений не доберётся.
Похоже здесь был бы уместен заголовок "ATtiny85 1001 применение по ua6em"
Можно ли настроить на вывод отрицательных температур?
Можно ли настроить на вывод отрицательных температур?
а индикация минуса и знакомест не хватает, можно конечно урезать до одного знака мантиссу но вывод знака минус остаётся под вопросом.
а индикация минуса и знакомест не хватает, можно конечно урезать до одного знака мантиссу но вывод знака минус остаётся под вопросом.
Ужас. Я на 3-х разрядный минус вывожу: -10 -9.9 0 0.5 36.6
Офигенные проблемы.
а индикация минуса и знакомест не хватает, можно конечно урезать до одного знака мантиссу но вывод знака минус остаётся под вопросом.
это потому что в коде не используется PLL и высокоскоростной таймер :)
это потому что в коде не используется PLL и высокоскоростной таймер :)
Как будто без PLL нельзя получить частоты СВ диапазона.)
это потому что в коде не используется PLL и высокоскоростной таймер :)
Как будто без PLL нельзя получить частоты СВ диапазона.)
можно, я сейчас в раздумьях как получить очень точный тайминг, на другой атмелине есть возможность включить часовой кварц для этого
Блинк на первом таймере:
// ATMEL ATtiny 25/45/85 // +-\/-+ // !RESET PCINT5 5/A0 (D 5) PB5 1| |8 Vcc // XTAL1 CLKI !OC1B PCINT3 3/A3 (D 3) PB3 2| |7 PB2 (D 2) 2/A1 PCINT2 USCK SCK T0 INT0 SCL // XTAL2 CLKO OC1B PCINT4 4/A2 {D 4) PB4 3| |6 PB1 (D 1) pwm1 PCINT1 MISO DO OC0B AIN1 // GND 4| |5 PB0 (D 0) pwm0 PCINT0 MOSI DI OC0A AIN0 SDA AREF // +----+ enum Prescalers { //T1CK PRESCALER_1 = 1, PRESCALER_2 = 2, PRESCALER_4 = 3, PRESCALER_8 = 4, PRESCALER_16 = 5, PRESCALER_32 = 6, PRESCALER_64 = 7, PRESCALER_128 = 8, PRESCALER_256 = 9, PRESCALER_512 = 10, PRESCALER_1024 = 11, PRESCALER_2048 = 12, PRESCALER_4096 = 13, PRESCALER_8192 = 14, PRESCALER_16384 = 15 }; void setup() { uint8_t prescaler = PRESCALER_16384; uint8_t topValue = 254; PLLCSR = 1<<LSM | 1<<PCKE | 1<<PLLE; // тактирование от PLL CLKx2 (4) DDRB |= (1 << DDB1) | (1 << DDB4); TCCR1 = prescaler | (1 << CTC1/*обнулить счетчик*/) | (1 << COM1A0/*переключить выходную линию*/); GTCCR = 1 << COM1B0/*переключить выходную линию*/; OCR1C = topValue; } void loop() {}Генератор импульсов: (библиотека здесь)
// ATMEL ATtiny 25/45/85 // +-\/-+ // !RESET PCINT5 5/A0 (D 5) PB5 1| |8 Vcc // XTAL1 CLKI !OC1B PCINT3 3/A3 (D 3) PB3 2| |7 PB2 (D 2) 2/A1 PCINT2 USCK SCK T0 INT0 SCL // XTAL2 CLKO OC1B PCINT4 4/A2 {D 4) PB4 3| |6 PB1 (D 1) pwm1 PCINT1 MISO DO OC0B AIN1 // GND 4| |5 PB0 (D 0) pwm0 PCINT0 MOSI DI OC0A AIN0 SDA AREF // +----+ // BLINK для ATtiny85 #include "Blink.h" uint8_t led01_pin = 0; uint8_t led02_pin = 1; //(LED_BUILTIN) uint8_t led03_pin = 2; uint16_t work_01_time = 2000; // включено (время включенного состояния мс) LED1 uint16_t pause_01 = 10000; // выключено (время выключенного состояния мс) LED1 uint16_t impuls_01 = 270; // число импульсов LED1 bool inverse_01 = true; uint16_t work_02_time = 250; // включено (время включенного состояния мс) LED2 uint16_t pause_02 = 300; // выключено(время выключенного состояния мс) LED2 uint16_t impuls_02 = 27; // число импульсов LED2 bool inverse_02 = false; uint16_t work_03_time = 10; // включено (время включенного состояния мс) LED3 uint16_t pause_03 = 10; // выключено(время выключенного состояния мс) LED3 uint16_t impuls_03 = 5000; // число импульсов LED3 bool inverse_03 = false; Blink leds_01(led01_pin, work_01_time, pause_01, impuls_01, inverse_01); Blink leds_02(led02_pin, work_02_time, pause_02, impuls_02); Blink leds_03(led03_pin); void setup() { leds_01.start(); leds_02.start(); leds_03.start(); } void loop() { leds_01.myrun(); leds_02.myrun(); leds_03.myrun(); }если значения в enum идут последовательно, необязательно присваивать каждое:
enum Prescalers { //T1CK PRESCALER_1 = 1, PRESCALER_2, PRESCALER_4, PRESCALER_8, PRESCALER_16, PRESCALER_32, PRESCALER_64, ..... };Еще бы калькулятор от Евгения Петровича к ATtiny адаптировать не помешало бы...
Еще бы калькулятор от Евгения Петровича к ATtiny адаптировать не помешало бы...
и что мешает?
У меня такая в пылесосе трудится.
На практике использовал данный МК один раз.
Заказ был на эмулятор датчика температуры, делал i2c slave устройство, вроде претензий от заказчика не было.
ЗЫ:ещё вспомнил для "ненастоящего" немца (или австрийца) управление семафором на макете железной дороги.
Столкнулся с непонятками! Есть простой блинк на ватчдог таймере, на значении 4096 - встряёт в состоянии HIGH, ядро и платка - DigiSpark
Код вот:
enum Wdt { //WDP(3-0)регистра WDTCR PRESCALER_16ms = 0, PRESCALER_32ms = 1, PRESCALER_64ms = 2, PRESCALER_128ms = 3, PRESCALER_256ms = 4, PRESCALER_512ms = 5, PRESCALER_1024ms = 6, PRESCALER_2048ms = 7, PRESCALER_4096ms = 8, PRESCALER_8192ms = 9 }; bool flash; // Watchdog interrupt ISR (WDT_vect) { WDTCR = WDTCR | 1 << WDIE;// |PRESCALER_4096ms; flash = !flash; } void setup() { pinMode(LED_BUILTIN, OUTPUT); WDTCR = 1 << WDIE | PRESCALER_4096ms; } void loop() { digitalWrite(LED_BUILTIN, flash); }Столкнулся с непонятками! Есть простой блинк на ватчдог таймере, на значении 4096 - встряёт в состоянии HIGH, ядро и платка - DigiSpark
Код вот:
enum Wdt { //WDP(3-0)регистра WDTCR PRESCALER_16ms = 0, PRESCALER_32ms = 1, PRESCALER_64ms = 2, PRESCALER_128ms = 3, PRESCALER_256ms = 4, PRESCALER_512ms = 5, PRESCALER_1024ms = 6, PRESCALER_2048ms = 7, PRESCALER_4096ms = 8, PRESCALER_8192ms = 9 }; bool flash; // Watchdog interrupt ISR (WDT_vect) { WDTCR = WDTCR | 1 << WDIE;// |PRESCALER_4096ms; flash = !flash; } void setup() { pinMode(LED_BUILTIN, OUTPUT); WDTCR = 1 << WDIE | PRESCALER_4096ms; } void loop() { digitalWrite(LED_BUILTIN, flash); }Оно вообще зачем ?
Тебе кто то запретил подключать "util/delay.h" ?
Почему тогда он тебе разрешил использовать digitalWrite() ?
Если то теме, то в строке 26 должно быть как минимум(с даташитом сверяться лениво):
WDTCR = (1 << WDIE) | (1<<PRESCALER_4096ms);
Сам же и отвечу, лоханулся со значением, WDP3 он в регистре 5-й бит )))
enum Wdt { //WDP(3-0)регистра WDTCR PRESCALER_16ms = 0, PRESCALER_32ms = 1, PRESCALER_64ms = 2, PRESCALER_128ms = 3, PRESCALER_256ms = 4, PRESCALER_512ms = 5, PRESCALER_1024ms = 6, PRESCALER_2048ms = 7, PRESCALER_4096ms = 32, PRESCALER_8192ms = 33 }; bool flash; uint8_t regs; // Watchdog interrupt ISR (WDT_vect) { WDTCR = WDTCR | 1 << WDIE; flash = !flash; } void setup() { pinMode(LED_BUILTIN, OUTPUT); regs = PRESCALER_4096ms; WDTCR = regs | 1 << WDIE; } void loop() { digitalWrite(LED_BUILTIN, flash); }BLINK на 1 таймере, используется встроенный LED DigiSpark.
Расчет таймеров:
Код программы:
enum Prescalers_1 { //T1CK PRESCALER_1 = 1, PRESCALER_2 = 2, PRESCALER_4 = 3, PRESCALER_8 = 4, PRESCALER_16 = 5, PRESCALER_32 = 6, PRESCALER_64 = 7, PRESCALER_128 = 8, PRESCALER_256 = 9, PRESCALER_512 = 10, PRESCALER_1024 = 11, PRESCALER_2048 = 12, PRESCALER_4096 = 13, PRESCALER_8192 = 14, PRESCALER_16384 = 15 }; void setup() { uint8_t prescaler1 = PRESCALER_16384; DDRB = (1 << PB1); OCR1A = 127; OCR1C = 255; TCCR1 = prescaler1 |(1 << PWM1A) | (1 << COM1A0);// | (1 << CS13) | (1 << CS12) | (1 << CS11)| (1 << CS10); } void loop() { }мое сообщение #28 игнорируете?
Ну сказали же, нафига енуму присвоение! Как то по калечному всё.)
Долго я писал.)))
мое сообщение #28 игнорируете?
а #34 как?
а #34 как?
enum Wdt { //WDP(3-0)регистра WDTCR PRESCALER_16ms , PRESCALER_32ms, PRESCALER_64ms , PRESCALER_128ms, PRESCALER_256ms, PRESCALER_512ms , PRESCALER_1024ms, PRESCALER_2048ms , PRESCALER_4096ms = 32, PRESCALER_8192ms = 33 };Для себя оставил для наглядности, что именно в регистр засунуть, код же будет по размеру одинаков надеюсь
"Горбатого могила исправит".( Так на ассемблере писать нужно!) А Wdt - это зачем?
Так на ассемблере писать нужно! А Wdt - это зачем?
так оттуда и тянется, 500 байт программы 16 килобайт листинг )))
Это я таймеры изучаю...
PS вообще-то подглядел у ЕвгенийП, а он плохому не научит )))
Задействованный wdt - значить код глючит, зависает а автор кода потерял надежду найти и исправить ошибки )))
Ну не соглашусь, всякие форс мажоры типа Эл магнитных помех никто не исключал, так что wdt пригодится всегда
Задействованный wdt ...
Не, не, не. Я об имени перечисления, которое нигде не используется.)
PS вообще-то подглядел у ЕвгенийП, а он плохому не научит )))
Может он в тот момент не знал, что за ним подглядывают.
Задействованный wdt - значить код глючит, зависает а автор кода потерял надежду найти и исправить ошибки )))
мне тут автор радиолюбительских спутников книжку подписал и я её даже прочитал, так там аж три типа WDT используется, поэтому AO-7 до сих пор еще живой )))
Задействованный wdt ...
Не, не, не. Я об имени перечисления, которое нигде не используется.)
мой косяк )))
мне тут автор радиолюбительских спутников книжку подписал и я её даже прочитал, так там аж три типа WDT используется, поэтому AO-7 до сих пор еще живой )))
В 2002-м воскрес, как я понял? И что работает кто то с ним? Как это выглядит?
мне тут автор радиолюбительских спутников книжку подписал и я её даже прочитал, так там аж три типа WDT используется, поэтому AO-7 до сих пор еще живой )))
В 2002-м воскрес, как я понял? И что работает кто то с ним? Как это выглядит?
отлично работает, у него зона покрытия более 1000км, высоко висит, да и наклон орбиты очень приличный, в высокие широты хорошо заходит... в 11-44 войдёт в зону радиосвязи в 11-57 покинет, там CW, если действовать оперативно пяток связей можно провести )))