diff --git a/README.md b/README.md index a5cc6a5..b803ba1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,71 @@ # 8. Устройство управления контроллером ШИМ. -## Устройство представляет из себя I2C ведущее устройство. Управляет устройством из задания 7. Имеет 6 кнопок: +## Описание + +### Устройство представляет из себя I2C ведущее устройство. + +### Управляет устройством из задания 7. Имеет 6 кнопок: - #### Включить ШИМ. - #### Выключить ШИМ. - #### Увеличить частоту на 25% от текущего. - #### Уменьшить частоту на 20% от текущего. -- #### Увеличить скважность. -- #### Уменьшить скважность. \ No newline at end of file +- #### Увеличить скважность. +- #### Уменьшить скважность. + +### Пояснения к заданию + +- #### Состояние ШИМ брать с Master устройста и передавать на Slave устройство. + +- #### Отправляемые команды - 16 бит, число с фиксированной запятой (12 бит целое, 4 - дробная часть). + +### Набор команд + +| Команда | Описание | +|:--------|:--------------------------------------| +| TODO: | Включить ШИМ. | +| TODO: | Выключить ШИМ. | +| TODO: | Увеличить частоту на 25% от текущего. | +| TODO: | Уменьшить частоту на 20% от текущего. | +| TODO: | Увеличить скважность. | +| TODO: | Уменьшить скважность. | + +### Описание регистров для I2C + +| Регистр | Описание | +|:--------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| TWBR | Отвечает за задание битрейта передачи данных. Его значение вычисляется по следующей формуле: ((F_CPU / I2C_FREQ) - 16) / 2. Формула производит округление результата. | +| TWCR | Регистр управления. Содержит в себе биты управления. | +| TWDR | Отвечает за передаваемые или принимаемые данные. В этом регистре хранится один байт данных в памяти. | +| TWAR | Cодержит адрес устройства, по которому микроконтроллер может быть доступен в сети I2C. | +| TODO: | N/A | + +### Описание флагов + +| Флаг | Описание | +|:------|:---------------------------------------------------------------------------------| +| TWINT | Флаг TWINT (TWI Interrupt Flag), показывающий, что операция TWI была выполнена. | +| TWEN | флаг TWEN (TWI Enable), включающий работу TWI интерфейса. | +| TWEA | флаг TWEA (TWI Enable Acknowledge), чтобы включить ответное подтверждение (ACK). | +| TODO: | N/A | + +### Иформация по TW + +**TWI (Two-Wire Interface)** - это двухпроводный последовательный интерфейс для обмена данными, основанный на +коммуникационном стандарте I2C (Inter-Integrated Circuit). TWI был разработан компанией Atmel (теперь Microchip +Technology) и является улучшенной версией I2C. + +TWI обеспечивает ориентированную на сообщения линию связи с возможностью мастер-ведомый обмен. Обычно в TWI существует +одно устройство, которое устанавливает шину (master), и одно или несколько устройств, которые подключаются к шине в +качестве активных (ved) узлов. + +TWI работает со скоростями передачи данных от 100 кбит/с до 400 кбит/с, хотя некоторые устройства могут поддерживать +скорость в 1 Мбит/с. + +Принцип работы TWI основан на использовании двух проводов - SDA (Serial Data) и SCL (Serial Clock). SDA - это линия для +передачи данных, а SCL - это линия для синхронизации передачи данных. Модулем TWI можно передавать данные и исполнять +команды чтения/записи, после чего мастер подтверждает успешность операции. + +TWI нашел широкое применение во многих устройствах, таких как датчики, LCD-экраны, термометры, акселерометры, сенсорные +панели, электронные блоки питания и т.д. + diff --git a/Wire.h b/Wire.h index 9b6c5a8..2c0373f 100644 --- a/Wire.h +++ b/Wire.h @@ -1,19 +1,15 @@ -// ========== I2C ========== #define I2C_FREQ 100000UL -void i2c_begin(uint8_t address) -{ +void i2c_begin(uint8_t address) { TWBR = ((F_CPU / I2C_FREQ) - 16) / 2; // Расчет предделителя для заданной частоты TWAR = (address << 1); // Установка адреса устройства } -void i2c_endTransaction() -{ +void i2c_endTransaction() { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Отправка условия STOP } -void i2c_beginTransmission(uint8_t address) -{ +void i2c_beginTransmission(uint8_t address) { TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Отправка условия START while (!(TWCR & (1 << TWINT))); // Ожидание завершения START @@ -22,9 +18,23 @@ void i2c_beginTransmission(uint8_t address) while (!(TWCR & (1 << TWINT))); // Ожидание завершения передачи адреса } -void i2c_write(uint8_t data) -{ + +void i2c_endTransmission() { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Отправка условия STOP +} + +void i2c_write(uint8_t data) { TWDR = data; // Запись данных TWCR = (1 << TWINT) | (1 << TWEN); // Отправка данных while (!(TWCR & (1 << TWINT))); // Ожидание завершения передачи данных +} + +void i2c_read(uint8_t *data, uint8_t ack) { + if (ack) { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); // Отправка ACK + } else { + TWCR = (1 << TWINT) | (1 << TWEN); // Отправка NACK + } + while (!(TWCR & (1 << TWINT))); // Ожидание завершения приема данных + *data = TWDR; // Чтение данных } \ No newline at end of file diff --git a/i2c.h b/i2c.h new file mode 100644 index 0000000..310f03c --- /dev/null +++ b/i2c.h @@ -0,0 +1,111 @@ +// +// Created by FSB-PC on 22.05.2023. +// + +#ifndef INC_8_PMW_I2C_H +#define INC_8_PMW_I2C_H + +typedef struct { + uint8_t address; // адрес устройства на шине I2C + uint8_t data[32]; // буфер для передачи и приема данных + uint8_t length; // длина передаваемых данных +}; + +void i2c_begin(i2c_device_t *i2c){ + TWBR = ((F_CPU / i2c->speed) - 16) / 2; + TWAR = (i2c->address << 1); +} + +void i2c_beginTransmission(i2c_device_t *i2c, uint8_t address){ + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); + while (!(TWCR & (1 << TWINT))); + + TWDR = (address << 1); + TWCR = (1 << TWINT) | (1 << TWEN); + while (!(TWCR & (1 << TWINT))); + i2c->address = address; +} + +void i2c_write(i2c_device_t *i2c, uint8_t data){ + TWDR = data; + TWCR = (1 << TWINT) | (1 << TWEN); + while (!(TWCR & (1 << TWINT))); +} + +void i2c_read(i2c_device_t *i2c, uint8_t *data, uint8_t ack){ + if (ack){ + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); + } else { + TWCR = (1 << TWINT) | (1 << TWEN); + } + while (!(TWCR & (1 << TWINT))); + *data = TWDR; +} + +void i2c_endTransmission(i2c_device_t *i2c){ + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); +} + +void i2c_sendByte(i2c_device_t *i2c, uint8_t address, uint8_t data){ + i2c_beginTransmission(i2c, address); + i2c_write(i2c, data); + i2c_endTransmission(i2c); +} + + + +#endif //INC_8_PMW_I2C_H + +//void i2c_begin(I2C *i2c) { +// TWBR = ((F_CPU / i2c->speed) - 16) / 2; +// TWAR = (i2c->address << 1); +//} +// +//void i2c_beginTransmission(I2C *i2c, uint8_t address) { +// TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); +// while (!(TWCR & (1 << TWINT))); +// +// TWDR = (address << 1); +// TWCR = (1 << TWINT) | (1 << TWEN); +// while (!(TWCR & (1 << TWINT))); +// i2c->address = address; +//} +// +//void i2c_write(I2C *i2c, uint8_t data) { +// TWDR = data; +// TWCR = (1 << TWINT) | (1 << TWEN); +// while (!(TWCR & (1 << TWINT))); +//} +// +//void i2c_read(I2C *i2c, uint8_t *data, uint8_t ack) { +// if (ack) { +// TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); +// } else { +// TWCR = (1 << TWINT) | (1 << TWEN); +// } +// while (!(TWCR & (1 << TWINT))); +// *data = TWDR; +//} +// +//void i2c_endTransmission(I2C *i2c) { +// TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); +//} +// +//void sendByte(uint8_t data) { +// sendByteRaw(data); +// _writes++; +// if (_writes >= 16) { +// endTransm(); +// beginData(); +// } +//} +// +//void sendByteRaw(uint8_t data) { +// i2c_write(data); +//} +// +//void sendCommand(uint8_t cmd1) { +// beginOneCommand(); +// sendByteRaw(cmd1); +// i2c_endTransmission(); +//} diff --git a/main.c b/main.c index fafbccf..482d356 100644 --- a/main.c +++ b/main.c @@ -1,93 +1,5 @@ -#include #define I2C_FREQ 100000UL +#include "i2c.h" +#include "pmw.h" -// Адрес устройства из задания 7 -const int deviceAddress = 8; -// Регистры устройства -byte reg1 = 0; -byte reg2 = 0; - -void setup() { - // Инициализация I2C - i2c_begin(); - // Назначение функции-обработчика для приема данных по I2C - Wire.onReceive(receiveData); - - // Настройка пинов для кнопок - pinMode(2, INPUT_PULLUP); - pinMode(3, INPUT_PULLUP); - pinMode(4, INPUT_PULLUP); - pinMode(5, INPUT_PULLUP); - pinMode(6, INPUT_PULLUP); - pinMode(7, INPUT_PULLUP); -} - -void loop() { - // Обработка нажатий кнопок - if (digitalRead(2) == LOW) { - // Включить ШИМ - reg1 |= 0b00000001; - sendData(); - } else if (digitalRead(3) == LOW) { - // Выключить ШИМ - reg1 &= 0b11111110; - sendData(); - } else if (digitalRead(4) == LOW) { - // Увеличить частоту на 25% - reg1 |= 0b00000010; - sendData(); - } else if (digitalRead(5) == LOW) { - // Уменьшить частоту на 20% - reg1 |= 0b00000100; - sendData(); - } else if (digitalRead(6) == LOW) { - // Увеличить скважность - reg2 |= 0b00000001; - sendData(); - } else if (digitalRead(7) == LOW) { - // Уменьшить скважность - reg2 &= 0b11111110; - sendData(); - } -} - -// Функция-обработчик для приема данных по I2C -void receiveData(int byteCount) { - while (Wire.available()) { - byte reg = Wire.read(); - byte value = Wire.read(); - // Запись значения в соответствующий регистр - if (reg == 1) { - reg1 = value; - // Включение ШИМ - if (bitRead(reg1, 0) == 1) { - analogWrite(9, 128); - } else { - analogWrite(9, 0); - } - // Изменение частоты ШИМ - if (bitRead(reg1, 1) == 1) { - analogWriteFrequency(9, 25000); - } else if (bitRead(reg1, 2) == 1) { - analogWriteFrequency(9, 8000); - } - } else if (reg == 2) { - reg2 = value; - // Изменение скважности ШИМ - int dutyCycle = map(reg2, 0, 255, 0, 1023); - analogWrite(9, dutyCycle); - } - } -} - -// Функция для отправки данных по I2C -void sendData() { - Wire.beginTransmission(deviceAddress); - // Отправка значений регистров - Wire.write(1); - Wire.write(reg1); - Wire.write(2); - Wire.write(reg2); - Wire.endTransmission(); -} \ No newline at end of file diff --git a/pwm.h b/pwm.h new file mode 100644 index 0000000..83ab59d --- /dev/null +++ b/pwm.h @@ -0,0 +1,70 @@ +// +// Created by FSB-PC on 22.05.2023. +// + +#ifndef INC_8_PMW_PMW_H +#define INC_8_PMW_PMW_H + + +struct PWM { + uint8_t pin; // номер пина + uint16_t frequency; // частота ШИМ сигнала + uint8_t duty_cycle; // коэффициент заполнения +}; + +void pwm_begin(struct PWM *pwm) { + TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01); + TCCR0B = (1 << CS00) | (1 << CS01); + OCR0A = 0; + pwm->duty_cycle = 0; +} + +void pwm_set_duty_cycle(struct PWM *pwm, uint8_t duty_cycle) { + pwm->duty_cycle = duty_cycle; + OCR0A = (duty_cycle * 255) / 100; +} + +void pwm_increase_speed(struct PWM *pwm) { + pwm->duty_cycle += 25; + TCCR0B = (1 << CS00) | (1 << CS01); +} + +void pwm_decrease_speed(struct PWM *pwm) { + pwm->duty_cycle -= 20; + TCCR0B = (1 << CS00) | (1 << CS01); +} + +void pwm_end(struct PWM *pwm) { + TCCR0B = 0; +} + + + +//void pmw_begin(PMW *pmw) { +// TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01); +// TCCR0B = (1 << CS00) | (1 << CS01); +// OCR0A = 0; +// pmw->speed = 0; +// pmw->duty_cycle = 0; +//} +// +//void pmw_set_duty_cycle(PMW *pmw, uint8_t duty_cycle) { +// pmw->duty_cycle = duty_cycle; +// OCR0A = (duty_cycle * 255) / 100; +//} +// +//void pmw_increase_speed(PMW *pmw) { +// pmw->speed += 25; +// TCCR0B = (1 << CS00) | (1 << CS01); +//} +// +//void pmw_decrease_speed(PMW *pmw) { +// pmw->speed -= 20; +// TCCR0B = (1 << CS00) | (1 << CS01); +//} +// +//void pmw_end(PMW *pmw) { +// TCCR0B = 0; +//} + +#endif //INC_8_PMW_PMW_H \ No newline at end of file diff --git a/test.c b/test.c new file mode 100644 index 0000000..452fa1f --- /dev/null +++ b/test.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +#define F_CPU 8000000UL +#define I2C_FREQ 100000UL + +#include "i2c.h" + +#define PWM_ADDRESS 0x50 +#define BUTTONS_ADDRESS 0x40 + +#define PWM_ENABLE_CMD 0x0001 +#define PWM_DISABLE_CMD 0x0002 +#define PWM_INCREASE_FREQ_CMD 0x0011 +#define PWM_DECREASE_FREQ_CMD 0x0012 +#define PWM_INCREASE_DUTY_CYCLE_CMD 0x0021 +#define PWM_DECREASE_DUTY_CYCLE_CMD 0x0022 + +struct PWM pwm; +struct I2C buttons_i2c; + +void setup() { + i2c_begin(&buttons_i2c); + i2c_beginTransmission(&buttons_i2c, BUTTONS_ADDRESS); + i2c_write(&buttons_i2c, 0x00); + i2c_endTransmission(&buttons_i2c); + + pwm.pin = PB3; + pwm.frequency = 1000; + pwm_begin(&pwm); +} + +void loop() { + i2c_beginTransmission(&buttons_i2c, BUTTONS_ADDRESS); + i2c_read(&buttons_i2c, buttons_i2c.data, 1); + i2c_endTransmission(&buttons_i2c); + + uint16_t command = ((uint16_t)(buttons_i2c.data[0]) << 8) | buttons_i2c.data[1]; + + if (command == PWM_ENABLE_CMD) { + pwm_increase_speed(&pwm); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 1); + } else if (command == PWM_DISABLE_CMD) { + pwm_decrease_speed(&pwm); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 0); + } else if (command == PWM_INCREASE_FREQ_CMD) { + pwm.frequency += round(pwm.frequency * 0.25); + pwm_set_frequency(&pwm, pwm.frequency); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 1); + } else if (command == PWM_DECREASE_FREQ_CMD) { + pwm.frequency -= round(pwm.frequency * 0.2); + pwm_set_frequency(&pwm, pwm.frequency); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 0); + } else if (command == PWM_INCREASE_DUTY_CYCLE_CMD) { + pwm_increase_duty_cycle(&pwm); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 1); + } else if (command == PWM_DECREASE_DUTY_CYCLE_CMD) { + pwm_decrease_duty_cycle(&pwm); + i2c_sendByte(&buttons_i2c, PWM_ADDRESS, 0); + } + + _delay_ms(100); +} + +int main() { + setup(); + while (true) { + loop(); + } + return 0; +} \ No newline at end of file