//VERSION #2 #include void pwm_init() { TCCR1A |= (1 << COM1A1) | (1 << WGM11); // non-inverting mode, Fast PWM (mode 14) TCCR1B |= (1 << WGM12) | (1 << WGM13) | (1 << CS10); // Fast PWM (mode 14), prescaler = 1 DDRB |= (1 << PB1); // set pin PB1 as output } void pwm_set_frequency(uint16_t frequency) { uint16_t prescaler = 1; uint32_t top = F_CPU / (prescaler * frequency) - 1; ICR1 = top; } void pwm_set_duty_cycle(uint8_t dutyCycle) { uint16_t value = ICR1 * dutyCycle / 100.0; OCR1A = value; } void pwm_enable() { pwm_set_duty_cycle(50); // Начальная скважность } void pwm_disable() { pwm_set_duty_cycle(0); // Выключение ШИМ } void pwm_init(); void pwm_set_frequency(uint16_t frequency); void pwm_set_duty_cycle(uint8_t dutyCycle); void pwm_enable(); void pwm_disable(); #define F_CPU 16000000UL #define I2C_FREQ 100000UL #define I2C_PRESCALER 1 #define I2C_BITRATE ((F_CPU / I2C_FREQ) - 16) / (2 * I2C_PRESCALER) void i2c_init() { TWBR = I2C_BITRATE; } void i2c_start() { // отправляем START bit TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // ожидаем пока START bit будет успешно отправлен while (!(TWCR & (1 << TWINT))); } void i2c_stop() { // отправляем STOP bit TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); // ожидаем пока STOP bit будет успешно отправлен while (TWCR & (1 << TWSTO)); } void i2c_write(uint8_t data) { // загружаем данные в регистр TWDR TWDR = data; // отправляем данные TWCR = (1 << TWINT) | (1 << TWEN); // ожидаем пока данные будут успешно отправлены while (!(TWCR & (1 << TWINT))); } uint8_t i2c_read_ack() { // разрешаем отправку ACK после прочтения байта TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); // ожидаем пока данные будут успешно прочитаны while (!(TWCR & (1 << TWINT))); // возвращаем прочитанный байт return TWDR; } uint8_t i2c_read_nack() { // запрещаем отправку ACK после прочтения байта TWCR = (1 << TWINT) | (1 << TWEN); // ожидаем пока данные будут успешно прочитаны while (!(TWCR & (1 << TWINT))); // возвращаем прочитанный байт return TWDR; } void i2c_init(); void i2c_start(); void i2c_stop(); void i2c_write(uint8_t data); uint8_t i2c_read_ack(); uint8_t i2c_read_nack(); const uint8_t BUTTON_PIN[] = {0, 1, 2, 3, 4, 5}; const uint8_t PWM_SLAVE_ADDR = 9; uint16_t command = 0x00; float frequency = 1000.0; float dutyCycle = 50.0; void setup() { i2c_init(); pwm_init(); pwm_enable(); // Включение ШИМ // Инициализируем пины кнопок DDRC &= ~(1 << PINC0) & ~(1 << PINC1) & ~(1 << PINC2) & ~(1 << PINC3) & ~(1 << PINC4) & ~(1 << PINC5); // подключены кнопки на пинах А0, А1, А2, А3, А4, А5 PORTC |= (1 << PINC0) | (1 << PINC1) | (1 << PINC2) | (1 << PINC3) | (1 << PINC4) | (1 << PINC5); // включение подтягивающего резистора Serial.begin(9600); // Инициализируем Serial монитор Serial.println("PWM Controller started!"); sendCommand(0x01, 0.0); // Включить ШИМ при запуске } void loop() { // Обработка нажатий на кнопки checkButton(0, "Turn on PWM!", 0x01, 0.0); checkButton(1, "Turn off PWM!", 0x02, 0.0); checkButton(2, "Increase frequency!", 0x03, 1.25); checkButton(3, "Decrease frequency!", 0x04, 0.8); checkButton(4, "Increase duty cycle!", 0x05, 1.1); checkButton(5, "Decrease duty cycle!", 0x06, 0.9); } void checkButton(uint8_t pin, const char* message, uint16_t cmd, float value) { if (bit_is_clear(PINC, pin)) { Serial.println(message); delay(500); sendCommand(cmd, value); if (cmd == 0x03) { frequency *= value; pwm_set_frequency(frequency); } else if (cmd == 0x04) { frequency *= value; pwm_set_frequency(frequency); } else if (cmd == 0x05) { dutyCycle *= value; pwm_set_duty_cycle(dutyCycle); } else if (cmd == 0x06) { dutyCycle *= value; pwm_set_duty_cycle(dutyCycle); } else if (cmd == 0x02) { pwm_disable(); // Выключение ШИМ } else if (cmd == 0x01) { pwm_enable(); // Включение ШИМ } command = cmd; } } void sendCommand(uint16_t cmd, float value) { if (cmd != 0x01 && cmd != 0x02) { // если команда изменения параметров switch (cmd) { case 0x03: // Увеличить частоту на 25% от текущего. value = frequency; value *= 0.25; break; case 0x04: // Уменьшить частоту на 20% от текущего. value = frequency; value *= 0.2; break; case 0x05: // Увеличить скважность на 10% от текущего. value = dutyCycle; value *= 0.1; break; case 0x06: // Уменьшить скважность на 10% от текущего. value = dutyCycle; value *= 0.1; break; } } // uint16_t data = (uint16_t)(value * 16.0); // data |= cmd << 4; // i2c_start(); // i2c_write(PWM_SLAVE_ADDR << 1); // i2c_write(data >> 8); // i2c_write(data & 0xFF); // i2c_stop(); // Serial.print("Sent command: "); // Serial.print(cmd, HEX); // Serial.print(", value: "); // Serial.println(value); uint16_t cmd_value = cmd << 12 | (uint16_t)(value * 16.0f); cmd_value |= cmd; i2c_start(); i2c_write(PWM_SLAVE_ADDR << 1); i2c_write(cmd_value >> 8); // старший байт команды i2c_write(cmd_value & 0xFF); // младший байт команды i2c_stop(); Serial.print("Sent command: 0x"); Serial.print(cmd, HEX); Serial.print(", value: "); Serial.print(value); Serial.print(", data bytes: 0x"); Serial.print(cmd_value >> 8, HEX); Serial.print(" "); Serial.println(cmd_value & 0xFF, HEX); }