evgenij-byvshev: Программирование микроконтроллеров. Часть 4-2 [3973619]


Делаем вольтметр

  И снова здравствуйте! В прошлой части этого курса я показал, как можно подключить LCD 16-и символьный 2-х строчный индикатор и инициализировать его. В этой части я покажу как выводить на него информацию.


Сейчас можно попробовать вывести что-нибудь на дисплей.
В главной функции main() напишем следующее:

LCD_ini(); //Инициализируем АЦП

    send_byte('G',1);
    send_byte('o',1);
    send_byte('l',1);
    send_byte('o',1);
    send_byte('s',1);

После компиляции программы попробуем запустить проект в Proteus:

Как видно – все прекрасно работает, но символы будут выводиться на одной строке последовательно друг за другом.
Для того чтобы можно было выводить информацию на экране дисплея в произвольном месте нужно написать еще одну функцию:

void set_pos(unsigned char x, unsigned y)
{
    char adress;
    adress=(0x40*y+x)|0b10000000;
    send_byte(adress, 0);
}

У этой функции два входных аргумента — позиция по горизонтали и позиция по вертикали.
В переменной address типа char находится вычисленное значение в зависимости от входных аргументов функции.
Значения входных аргументов мы будем начинать от нуля, а не от единицы. Поэтому чтобы вычислить адрес по y в памяти DDRAM, нам достаточно умножить значение y на 0x40, так как именно с данного адреса начинается 2 строка, а у нас она будет выглядеть как 1. Затем прибавляем x, тем самым получим смещение по горизонтали в данной памяти. И ещё по операции "ИЛИ" вычисленное значение мы сложим с двоичным числом 0b10000000. То есть мы передаём единицу для того, чтобы контроллер дисплея "понял" что мы даём именно такую команду — передача адреса памяти DDRAM, чтобы контроллер дисплея установил указатель именно туда, какую позицию мы даём ему в оставшихся семи младших битах — DB6-DB0.


Чтобы проверить как это работает нужно написать следующий код:

LCD_ini(); //Инициализируем АЦП

    set_pos(5,0);  //Печатаем в первой строке
    send_byte('H',1);
    send_byte('e',1);
    send_byte('l',1);
    send_byte('l',1);
    send_byte('o',1);
    send_byte(',',1);

    set_pos(5,1);  //Печатаем во второй строке
    send_byte('G',1);
    send_byte('o',1);
    send_byte('l',1);
    send_byte('o',1);
    send_byte('s',1);
    send_byte('!',1);

Результат работы в программе Proteus:

Таким способом можно выводить на экран любой символ на экран дисплея по одному. Для того чтобы вывести на экран сразу целую строку нужно написать две функции.
В первой функции просто вызывается другая функция с единичкой, чтобы каждый раз не писать единички в которой будут передаваться только данные:

void send_char(unsigned char c)
{
    send_byte(c,1);
}

Во второй функции в цикле попеременно будут перебираться символы и выводиться на экран:

void str_lcd (char str1[])
{
    wchar_t n;
    for(n=0;str1[n]!='\0';n++)
    send_char(str1[n]);
}

И напишем код для проверки:

LCD_ini(); //Инициализируем АЦП

set_pos(3,0);  //Печатаем в первой строке
str_lcd("Hello,");

set_pos(8,1);  //Печатаем во второй строке
str_lcd("Golos!");

Результат работы в Proteus:

С выводом информации мы разобрались, теперь нужно вывести информацию, полученную с АЦП предварительно ее обработав для понятного нам вида. Для этого нужно в теле главного цикла добавить следующий код:

V = (float) u*0.0048828; // Переводим в вольты
sprintf(string, "V = %1.2f", V); // форматируем
set_pos(0,1);  //Выставляем курсор
str_lcd(string); //Выводим данные на дисплей

V = (float) u*0.0048828; Здесь мы преобразуем полученное число в вольты. Так
как у нас опорное напряжение 5В, а значение регистра 1024, то мы 5/1024=0.0048828
Это коэффициент напряжения. Ну или минимальная величина напряжения при минимальном
значении регистра ADCW. То есть если в регистре будет значение 1, то величина напряжения
будет равна 0.0048828 В. Поэтому мы в строке, данные ADCW перемножаем с 0.0048828.
Слово float в скобке нужно для того чтобы преобразовать переменную u из целочисленной
в вещественную с плавающей точкой.


sprintf(string, "V = %1.2f", V); Здесь мы заносим значение напряжения в массив
string с форматированием. Сначала мы впишем V = . После ставится знак процента.
Он говорит о том сколько знаков будет выведено. 1.2f говорит о том, что мы хотим
вывести один знак до запятой и 2 знака после, а буква f говорит, что мы имеем дело
со значением вещественным с плавающей точкой.


А в остальных двух строках все ясно из комментариев
set_pos(0,1); //Выставляем курсор
str_lcd(string); //Выводим данные на дисплей


Результат моделирования схемы в Proteus:


Полный текст программы:

#define F_CPU 8000000UL //Рабочая частота МК (8МГц)
#include <avr/io.h>
#include <stdio.h> //Нужна для работы с текстом
#include <stdlib.h> //заголовочный файл стандартной библиотеки языка Си
#include <util/delay.h> //для _delay_ms()
#define e1 PORTB|=0b00001000 //установка линии E в 1
#define e0 PORTB&=0b11110111 //установка линии E в 0
#define rs1 PORTB|=0b00000100 //установка линии RS в 1 (данные)
#define rs0 PORTB&=0b11111011 //установка линии RS в 0 (команда)

char string[10]; //Массив для временного хранения форматированного текста

//Инициализируем порты
void port_ini(void)
{
    DDRD=0xff;
    PORTD=0x00;
    DDRB=0xFF;
    PORTB=0x00;
}

//Отправляем половину байта
void send_half_byte(unsigned char c)
{
    c< //включаем линию  //стираем информацию на входах DB4-DB7, остальное не  //выключаем линию Е
    _delay_us(50);
}

void send_byte(unsigned char c, unsigned char mode)
{
    if  rs0;
    else         rs1;
    unsigned char >>4;
    send_half_byte(hc); send_half_byte(c);
}

//Передаем только данные
void send_char(unsigned char c)
{
    send_byte(c,1);
}

//Задаем позицию
void set_pos(unsigned char x, unsigned y)
{
    char adress;
    adress=(0x40*y+x)|0b10000000;
    send_byte(adress, 0);
}

//Выводим строку
void str_lcd (char str1[])
{
    wchar_t n;
    for(n=0;str1[n]!='\0';n++)
    send_char(str1[n]);
}

//Инициализация LCD
void LCD_ini(void)
{
    _delay_ms(15); //Ждем 15 мс
    send_half_byte(0b00000011);
    _delay_ms(4);
    send_half_byte(0b00000011);
    _delay_us(100);
    send_half_byte(0b00000011);
    _delay_ms(1);
    send_half_byte(0b00000010);
    _delay_ms(1);
    send_byte(0b00101000, 0); //4бит-режим и 2 линии
    _delay_ms(1);
    send_byte(0b00001100, 0); //включаем изображение на дисплее, курсоры никакие не включаем
    _delay_ms(1);
    send_byte(0b00000110, 0); //курсор (хоть он у нас и невидимый) будет двигаться влево
    _delay_ms(1);
}

//Инициализируем АЦП
void ADC_ini(){
    ADCSRA |= (1<<ADEN) // Разрешение использования АЦП
    |(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//Делитель 128
    ADMUX  //Подключен внешний опорный ион, внутренний ион выключен
    ADMUX  << MUX0)|(0 << MUX1)|(0 << MUX2)|(0 << MUX3); // вход PC0
}

//Получаем данные с АЦП
float ADC_convert (void)
{
    ADCSRA  //Начинаем преобразование
    while((ADCSRA & (1<<ADSC))); //проверим закончилось ли аналого-цифровое преобразование
    return (unsigned int) ADC;
}

//Главная функция
int main(void)
{
   port_ini(); //Инициализируем порты
   ADC_ini(); //Инициализируем АЦП
   LCD_ini(); //Инициализируем АЦП

    set_pos(2,0);  //Устанавливаем курсор
    str_lcd("Hello, Golos");

    unsigned int  u;
    float V;  // Переменная для выводимого значения. float так как у нас точность до 2 знаков.

    while (1) 
    {

        u  //Вызывем преобразование
        V  u*0.0048828; // Переводим в вольты
        sprintf(string, "V  V); // форматируем
        set_pos(0,1);  //Выставляем курсор
        str_lcd(string); //Выводим данные на дисплей

        //проверяем считанное значение
        if (u > 128)
        PORTD = 0b00000001;
        else
        PORTD = 0b00000000;
        if (u > 256) 
        PORTD = 0b00000011;
        if (u > 384)
        PORTD = 0b00000111;
        if (u > 512)
        PORTD = 0b00001111;
        if (u > 640)
        PORTD = 0b00011111;
        if (u > 768) 
        PORTD = 0b00111111;
        if (u > 896) 
        PORTD = 0b01111111;
        if (u > 1020)
        PORTD = 0b11111111;
        _delay_ms(30);
    }
}


Если есть вопросы и предложения по этому уроку, пишите комментарии буду рад ответить на ваши вопросы.


Часть 1
Часть 2-1 Часть 2-2
Часть 3-1 Часть 3-2
Часть 4-1


Мой блог в ЖЖ: http://evgenij-byvshev.livejournal.com


Оригинал поста создан 05-03-2017 15:43:12 UTC


3972186 Поездка на Гору Обезьян. Пхукет
3972278 Golos -- это прорыв! Facebook напрягся!
3972404 Прага. Командировочные заметки
3972500 Конкурс Геоазбука - Антекера
3972661 Отгадка - домик для насекомых.
3972695 Только что вернулся со "Зверопоя"
3972709 Тест AZ - Screen Recorder. Захват аудио и видео на смартфоне. Первый опыт.
3973377 20 способностей для начала домашнего бизнеса
3973404 Зависимость профита робота от стандартного отклонения цены и скользящей от объемов торгов NAUT
3973427 Прага. Командировочные заметки (продолжение)
3973619 Программирование микроконтроллеров. Часть 4-2
3973986 Не успел
3974556 Заброшенный Питер
3974560 О природе конфликтов, Голосе и Свободе Воли.
3974647 Игра №6 Угадай-ка! Победитель: @vas

Прежде чем писать комментарий прочитайте О ПРОЕКТЕ
Поддержите проект донатом!


Comments 0