#include void pwm_init() { // Configure pin PB1 as output DDRB |= (1 << PB1); // Configure Timer1 in Fast PWM (mode 14) with non-inverting mode TCCR1A |= (1 << COM1A1) | (1 << WGM11); TCCR1B |= (1 << WGM12) | (1 << WGM13) | (1 << CS10); } void pwm_set_frequency(uint16_t frequency) { // Calculate the top value for Timer1 uint16_t prescaler = 1; uint32_t top = F_CPU / (prescaler * frequency) - 1; ICR1 = top; } void pwm_set_duty_cycle(uint8_t duty_cycle) { // Calculate the OCR1A value corresponding to the duty cycle uint16_t value = ICR1 * duty_cycle / 100; OCR1A = value; } void pwm_enable() { pwm_set_duty_cycle(50); // Initial duty cycle } void pwm_disable() { pwm_set_duty_cycle(0); // Turn off PWM } uint16_t pwm_get_frequency() { uint16_t prescaler = 1; uint32_t frequency = F_CPU / (prescaler * (ICR1 + 1)); return frequency; } uint8_t pwm_get_duty_cycle() { uint8_t duty_cycle = OCR1A * 100 / ICR1; return duty_cycle; } #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; } 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!"); Serial.println("PWM on master device initial successful!"); 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 == 0x03 || cmd == 0x04) { value = pwm_get_frequency(); // Если да, получаем текущее значение частоты } else if (cmd == 0x05 || cmd == 0x06) { value = pwm_get_duty_cycle(); // Если да, получаем текущее значение коэффициента заполнения } // Кодируем команду и значение в 16-битное число uint16_t cmd_value = cmd << 12 | (uint16_t)(value * 16.0f); // Отправляем команду через шину I2C 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); }