#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#include <stdbool.h>
#define ADC_INT_ON ((1 << ADIE) | (1 << ADIF))
#define ADC_INT_OFF (~(1 << ADIE))
#ifndef F_CPU
#define F_CPU 16000000UL // 16 МГц
#endif
//Настройка таймера 1 в режим СТС для опроса модуля сенсора по прерыванию
void timer1_setup(void){
TCCR1B |= (1 << CS12) | (1 << WGM12);
TIMSK1 |= (1 << OCIE1A);
OCR1A = 624;
}
//Настройка I2C - сенсор
void i2c_setup(void){
//Выставляем 400кГц
TWBR = 12; TWSR &= (1 << TWPS1); TWSR &= (1 << TWPS0);
}
//Настройка SPI - дисплей
void spi_setup(void){
SPCR = (1 << SPE); //Включаем модуль
SPCR |= (1 << SPI2X); //Удваиваем скорость
//Настраиваем подключенные пины как выходы
DDRH |= (1 << PH5) | (1 << PH6) | (1 << PH7); //RST, D/C, CS
DDRB = (1 << PB2) | (1 << PB1); //MOSI, SCK
PORTB |= (1 << PB3); //Включаем подтяжку
PORTB &= ~(1 << PH5); //Стартовая перезагрузка дисплея записью RST = 0
PORTB &= ~(1 << PH7); //Выбираем для работы именно дисплей
}
//Глобальный флаг отсутствия касаний
volatile bool is_no_touch = true;
//Глобальная переменная для хранения координат касания
volatile uint32_t coordinates[2] = {0, 0};
//Глобальный флаг готовности координат касания
volatile bool ready_coordinates = false;
//Глобальный флаг завершения опроса модуля
//Функция для сброса флага, включения модуля, ожидания сброса флага и проверки состояния TWSR
bool reset_flag(uint16_t register, uint8_t flag){
if(flag == 1){ TWCR |= (1 << TWEN) | (1 << TWINT); }
else{ TWCR = (1 << TWEN) | (1 << TWINT); }
while(!(TWCR & (1 << TWINT))); //Ждем сброса флага завершения текущей операции
return ((TWSR & 0xF8) == register);//Проверка регистра
}
//Получаем координаты касания
void take_coordinates(void){
//Переменные для битов координат
uint8_t x_11_4 = 0; uint8_t x_3_0 = 0; uint8_t y_11_4 = 0; uint8_t y_3_0 = 0;
//1. Запуск операции Start
TWCR = (1 << TWSTA); if(!reset_flag(0x08, 1)){ return; }
//2. Отправляем адрес конкретного устройства на шине с младшим битом = 0 - запись
TWDR = (0x38 << 1) | 0; if(!reset_flag(0x40, 1)){ return; }
//3. Отправляем адрес регистра для чтения
TWDR = 0x02; //Регистр касаний
if(!reset_flag(0x28, 0)){ return; }//Проверка регистра на получение ASK
//4. Меняем направление при помощи повторного старта
//Повторно сбрасываем флаг
TWCR = (1 << TWSTA); if(!reset_flag(0x10, 1)){ return; }
//5. Отправка адреса устройства для чтения( младший бит = 1 - флаг чтения)
TWDR = (0x38 << 1) | 1; if(!reset_flag(0x18, 0)){ return; } // Проверка регистра на получение ASK
//6. Чтение регистра касаний
TWCR = (1 << TWEA); if(!reset_flag(0x50, 1)){return;} // Проверка регистра на получение ASK
if(TWDR == 0x00){ //Проверка на наличие нажатий
is_no_touch = true;
TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT);
while(TWCR & (1 << TWSTO)); return;}
//7. Чтение старшего байта абсциссы
TWCR = (1 << TWEA); if(!reset_flag(0x50, 1)){return;} // Проверка регистра на получение ASK
x_11_4 = TWDR; //Считываем значение регистра данных
//8. Чтение младшего байта абсциссы
TWCR = (1 << TWEA); if(!reset_flag(0x50, 1)){return;}
x_3_0 = TWDR; //Считываем значение регистра данных
//9. Чтение старшего байта ординаты
TWCR = (1 << TWEA); if(!reset_flag(0x50, 1)){return;}
y_11_4 = TWDR; //Считываем значение регистра данных
//10. Чтение младшего байта ординаты
if(!reset_flag(0x58, 0)){return;} // Проверка регистра на получение NASK
y_3_0 = TWDR; //Считываем значение регистра данных
//11. Генерация сигнала стоп
TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT);
while(TWCR & (1 << TWSTO));
//Записываем координаты путем объединения битов при помощи маски
coordinates[0] = ( (x_11_4 << 4) | x_3_0); coordinates[1] = ( (y_11_4 << 4) | y_3_0);
ready_coordinates = true;
}
//Функция для передачи одного байта
void take_data(uint8_t data){
SPDR = data; //Записываем данные в регистр данных
while(!(SPSR & (1 << SPIF))); //Ждем поднятия флага завершения передачи
uint8_t res = SPDR; //Читаем полученные в ответ данные
}
//Функция для закрашивания пикселя
void draw_pixel(uint16_t x, uint16_t y, uint16_t color){
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2A); //Команда для установки границ по абсциссе
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
take_data((x >> 8) & 0xFF); //Передаем старший байт левой границы
take_data(x & 0xFF); //Передаем младший байт
take_data((x >> 8) & 0xFF); //Передаем старший байт правой границы
take_data(x & 0xFF); //Передаем младший байт
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2B); //Команда для установки границ по ординате
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
take_data((y >> 8) & 0xFF); //Передаем старший байт нижней границы
take_data(y & 0xFF); //Передаем младший байт
take_data((y >> 8) & 0xFF); //Передаем старший байт верхней границы
take_data(y & 0xFF); //Передаем младший байт
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2C); //Команда для непрерывной записи в GRAM
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
take_data(color >> 8); take_data(color & 0xFF); //Цвет
}
//Функция для отрисовки 8 симметричных точек
void draw_circle_points(int16_t cx, int16_t cy, int16_t x, int16_t y, uint16_t color) {
draw_pixel(cx + x, cy + y, color);
draw_pixel(cx - x, cy + y, color);
draw_pixel(cx + x, cy - y, color);
draw_pixel(cx - x, cy - y, color);
draw_pixel(cx + y, cy + x, color);
draw_pixel(cx - y, cy + x, color);
draw_pixel(cx + y, cy - x, color);
draw_pixel(cx - y, cy - x, color);
}
//Функция для вывода прямоугольника
void draw_rectangle(uint16_t x_min, uint16_t y_min, uint16_t x_max, uint16_t y_max, uint16_t color){
SPCR |= (1 << MSTR); //Режим Master
// 1. Обозначаем границы прямоугольника
// 1.1 Устанавливаем границы по абсциссе
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2A); //Команда для установки границ по абсциссе
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
take_data((x_min >> 8) & 0xFF); //Передаем старший байт левой границы
take_data(x_min & 0xFF); //Передаем младший байт
take_data((x_max >> 8) & 0xFF); //Передаем старший байт правой границы
take_data(x_max & 0xFF); //Передаем младший байт
// 1.2 Устанавливаем границы по ординате
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2B); //Команда для установки границ по ординате
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
take_data((y_min >> 8) & 0xFF); //Передаем старший байт нижней границы
take_data(y_min & 0xFF); //Передаем младший байт
take_data((y_max >> 8) & 0xFF); //Передаем старший байт верхней границы
take_data(y_max & 0xFF); //Передаем младший байт
// 2. Закрашиваем прямоугольник
PORTH &= ~(1 << PH6); // D/C = 0 чтобы подать команду
take_data(0x2C); //Команда для непрерывной записи в GRAM
PORTH |= (1 << PH6); // D/C = 1 чтобы подать данные
//Закрашиваем
for(int i = 0; i < (y_max + x_max - x_min - y_min + 2); i++){
take_data(color >> 8); take_data(color & 0xFF); //Цвет
}
}
//Функция для вывода круга
void draw_circle(int16_t cx, int16_t cy, uint16_t r, uint16_t color) {
int16_t x = 0; // текущая координата X (начинаем с 0)
int16_t y = r; // текущая координата Y (начинаем с радиуса)
int16_t d = 1 - r; // начальное значение параметра решения (ошибка)
draw_circle_points(cx, cy, x, y, color);
// Основной цикл: идём по X от 0 до тех пор, пока x <= y
while (x < y) {
x++; // увеличиваем x на 1
// Проверяем, нужно ли уменьшить y
// Если d < 0, то ошибка показывает, что текущий y всё ещё подходит
// Если d >= 0, то нужно уменьшить y на 1 и скорректировать ошибку
if (d < 0) {
d += 2 * x + 1; // коррекция ошибки для случая, когда y не меняется
} else {
y--;
d += 2 * (x - y) + 1; // коррекция ошибки для случая, когда y уменьшается
}
// Отрисовываем 8 симметричных точек для новых (x, y)
draw_circle_points(cx, cy, x, y, color);
}
}
//Структура для кнопки
struct Button{
uint16_t x = 0; uint16_t y = 0; uint16_t row = 0; uint16_t hight = 0;
uint16_t color; void (*func)(void);
}
int main(void) {
spi_setup(); i2c_setup(); timer1_setup(); sei();
while (1) {
}
}