8.Master_I2C_PWM/main.cpp
2023-06-22 15:41:24 +03:00

193 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <avr/io.h>
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);
}