#define F_CPU 16000000UL #define I2C_FREQ 100000UL #define I2C_PRESCALER 1 #define I2C_BITRATE ((F_CPU / I2C_FREQ) - 16) / (2 * I2C_PRESCALER) #include #include #include float pwm_frequency = 1.0; uint8_t pwm_duty_cycle = 50; volatile uint8_t pwm_enabled = 0; volatile uint8_t pwm_changed = 0; void i2c_init() { TWBR = I2C_BITRATE; } void i2c_start() { TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); while (!(TWCR & (1 << TWINT))) ; } void i2c_stop() { TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); while (TWCR & (1 << TWSTO)) ; } void i2c_write(uint8_t data) { TWDR = data; TWCR = (1 << TWINT) | (1 << TWEN); while (!(TWCR & (1 << TWINT))) ; } uint8_t i2c_read_ack() { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); while (!(TWCR & (1 << TWINT))) ; return TWDR; } uint8_t i2c_read_nack() { TWCR = (1 << TWINT) | (1 << TWEN); while (!(TWCR & (1 << TWINT))) ; return TWDR; } const uint8_t PWM_SLAVE_ADDR = 9; void pwm_enable() { pwm_enabled = 1; pwm_changed = 1; } void pwm_disable() { pwm_enabled = 0; pwm_changed = 1; } void setPWMFrequency(float frequency) { if (frequency < 0 || frequency > 50000.0) { return; // Недопустимая частота } pwm_frequency = frequency; pwm_changed = 1; } void setPWMDutyCycle(float duty_cycle) { if (duty_cycle < 1 || duty_cycle > 100) { return; // Недопустимый коэффициент заполнения } pwm_duty_cycle = duty_cycle; pwm_changed = 1; } void pwm_check_state() { if (pwm_enabled) { // Включаем пин ШИМ - реализация зависит от аппаратной платформы Serial.print("PWM enabled. Frequency: "); Serial.print(pwm_frequency); Serial.print(" Hz, duty cycle: "); Serial.println(pwm_duty_cycle); } else { // Выключаем пин ШИМ - реализация зависит от аппаратной платформы Serial.println("PWM disabled"); } } void pwm_set_frequency(float frequency) { // Устанавливаем частоту ШИМ - реализация зависит от аппаратной платформы Serial.print("Setting PWM frequency to: "); Serial.println(frequency); } void pwm_set_duty_cycle(float duty_cycle) { // Устанавливаем коэффициент заполнения ШИМ - реализация зависит от аппаратной платформы Serial.print("Setting PWM duty cycle to: "); Serial.println(duty_cycle); } void enablePWM() { pwm_enable(); // Включаем ШИМ sendCommand(0x01, 0.0); } void disablePWM() { // Выключаем ШИМ sendCommand(0x02, 0.0); pwm_disable(); } void increaseFrequency() { setPWMFrequency(pwm_frequency * 1.25); // Выключаем ШИМ, отправляем команду и включаем обратно disablePWM(); sendCommand(0x03, pwm_frequency); enablePWM(); } void decreaseFrequency() { setPWMFrequency(pwm_frequency * 0.8); // Выключаем ШИМ, отправляем команду и включаем обратно disablePWM(); sendCommand(0x04, pwm_frequency); enablePWM(); } void increaseDutyCycle() { if (pwm_duty_cycle < 100) { pwm_duty_cycle+=10; setPWMDutyCycle(pwm_duty_cycle); // Выключаем ШИМ, отправляем команду и включаем обратно disablePWM(); sendCommand(0x05, pwm_duty_cycle); enablePWM(); } else { Serial.println("Maximum duty cycle reached!"); } } void decreaseDutyCycle() { if (pwm_duty_cycle > 1) { pwm_duty_cycle-=10; setPWMDutyCycle(pwm_duty_cycle); // Выключаем ШИМ, отправляем команду и включаем обратно disablePWM(); sendCommand(0x06, pwm_duty_cycle); enablePWM(); } else { Serial.println("Minimum duty cycle reached!"); } } void checkButton(uint8_t pin, const char* message, void (*command)()) { if (bit_is_clear(PINC, pin)) { Serial.println(message); delay(200); command(); } // Обновляем состояние ШИМ, если что-то изменилось if (pwm_changed) { pwm_changed = 0; pwm_check_state(); if (pwm_enabled) { pwm_set_frequency(pwm_frequency); pwm_set_duty_cycle(pwm_duty_cycle); } } } void setup() { i2c_init(); DDRC &= ~(1 << PINC0) & ~(1 << PINC1) & ~(1 << PINC2) & ~(1 << PINC3) & ~(1 << PINC4) & ~(1 << PINC5); PORTC |= (1 << PINC0) | (1 << PINC1) | (1 << PINC2) | (1 << PINC3) | (1 << PINC4) | (1 << PINC5); Serial.begin(9600); Serial.println("PWM Controller started!"); sendCommand(0x01, 0.0); } void loop() { checkButton(0, "Turn on PWM!", enablePWM); checkButton(1, "Turn off PWM!", disablePWM); checkButton(2, "Increase frequency by 25%!", increaseFrequency); checkButton(3, "Decrease frequency by 20%!", decreaseFrequency); checkButton(4, "Increase duty cycle by 10%!", increaseDutyCycle); checkButton(5, "Decrease duty cycle by 10%!", decreaseDutyCycle); } void sendCommand(uint8_t cmd, float value) { uint16_t cmd_value; switch (cmd) { case 0x01: case 0x02: sendCommandOneByte(cmd); break; case 0x03: case 0x04: sendCommandThreeBytes(cmd, value); break; case 0x05: case 0x06: sendCommandTwoBytes(cmd, value); break; default: Serial.println("Unknown command"); break; } } void sendCommandOneByte(uint8_t cmd) { i2c_start(); i2c_write(PWM_SLAVE_ADDR << 1); i2c_write(cmd); i2c_stop(); Serial.print("Sent command: 0x"); Serial.println(cmd, HEX); } void sendCommandTwoBytes(uint8_t cmd, float value) { uint16_t cmd_value = round(value); i2c_start(); i2c_write(PWM_SLAVE_ADDR << 1); i2c_write(cmd); i2c_write(cmd_value & 0xFF); i2c_stop(); Serial.print("Sent command: 0x"); Serial.print(cmd, HEX); Serial.print(", value: "); Serial.print(value); Serial.print(", cmd value: "); Serial.print(cmd_value); Serial.print(", data bytes: 0x"); Serial.println(cmd_value & 0xFF, HEX); } void sendCommandThreeBytes(uint8_t cmd, float value) { uint16_t cmd_value = round(value * 16); // приводим к 12 бит и 4 бита дробной части uint8_t data_bytes[2] = {(cmd_value >> 8) & 0xFF, cmd_value & 0xFF}; i2c_start(); i2c_write(PWM_SLAVE_ADDR << 1); i2c_write(cmd); i2c_write(data_bytes[0]); i2c_write(data_bytes[1]); i2c_stop(); Serial.print("Sent command: 0x"); Serial.print(cmd, HEX); Serial.print(", value: "); Serial.print(value); Serial.print(", cmd value: "); Serial.print(cmd_value); Serial.print(", data bytes: 0x"); Serial.print(data_bytes[0], HEX); Serial.print(" "); Serial.println(data_bytes[1], HEX); }