#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; float pwm_duty_cycle = 0.5; 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 < 1.0 || frequency > 50000.0) { return; // Недопустимая частота } pwm_frequency = frequency; pwm_changed = 1; } void setPWMDutyCycle(float duty_cycle) { if (duty_cycle < 0.0 || duty_cycle > 1.0) { 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 < 0.9) { setPWMDutyCycle(pwm_duty_cycle * 1.1); // Выключаем ШИМ, отправляем команду и включаем обратно disablePWM(); sendCommand(0x05, pwm_duty_cycle); enablePWM(); } else { Serial.println("Maximum duty cycle reached!"); } } void decreaseDutyCycle() { if (pwm_duty_cycle > 0.1) { setPWMDutyCycle(pwm_duty_cycle*0.9); // Выключаем ШИМ, отправляем команду и включаем обратно 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(1000); command(); } } 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); // Обновляем состояние ШИМ, если что-то изменилось 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 sendCommand(uint8_t cmd, float value) { // Приводим значение к 12 битам для целой части и 4 битам для дробной части uint16_t integer_part = (uint16_t)value; uint8_t fractional_part = (uint8_t)((value - integer_part) * 16); // Проверяем, чтобы значение не превышало допустимых границ if (integer_part > 0xFFF) { integer_part = 0xFFF; } if (fractional_part > 0xF) { fractional_part = 0xF; } // Формируем 4 байта данных для отправки uint8_t upper_byte = (integer_part >> 4) & 0xFF; uint8_t lower_byte = ((integer_part & 0xF) << 4) | (fractional_part & 0xF); i2c_start(); i2c_write(PWM_SLAVE_ADDR << 1); i2c_write(cmd); i2c_write(upper_byte); i2c_write(lower_byte); i2c_stop(); Serial.print("Sent command: 0x"); Serial.print(cmd, HEX); Serial.print(", value: "); Serial.print(value); Serial.print(", cmd value: "); Serial.print((integer_part << 4) | fractional_part); Serial.print(", data bytes: 0x"); Serial.print(upper_byte, HEX); Serial.print(" "); Serial.println(lower_byte, HEX); }