Усилитель. Шаг 1. Мозги.

Начинаем продвижение по созданию усилителя. Первым делом я решил собирать мозги, так как именно от них зависит весь функционал периферии и эта часть имеет прямое отношение к тематике блога. Итак, что мы имеем.

  • PIC16f877a
  • DS1307
  • DS18b20

Что требуется получить в итоге?

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

Для начала определимся с протоколами, в данном случае мы используем три протокола передачи данных:

  1. I2C – главный протокол общения;
  2. 1-wire – связь с цифровым термометром DS18b20;
  3. USART – связь с компьютером.

Как всегда дело начинается с составления схемы:

По схеме из особенных моментов можно выделить 2:

  1. Разъемы Р2, Р14 предусмотрены для питания платы напрямую, без стабилизации, либо с внутренней стабилизацией.
  2. На разъем В предусмотрена возможность подключения кнопки к любому выводу либо использования его как обычный порт ввода/вывода.

В итоге получаем новую devboard с pic16f877a на борту, модернизируя которую можем добавлять новые функции для усилителя в любой момент. Ну еще пару фоток получившейся платы:

Надо будет освоить технологию лужения в домашних условиях, а то паяльником водить не сильно красиво получается…

Пока все разъемы не запаивал, т.к. в них еще нет необходимости, вот когда буду в корпус засовывать припаяю надежные разъемы, с помощью которых провода можно надежно зажать.

В принципе по схеме все, больше никаких особенностей – все стандартно, так что можно переходить к описанию программной реализации.

Начнем с I2C: можно было бы пойти проторенным путем софтварного i2c, но зачем растрачивать драгоценные ресурсы мк, если у нас на борту есть хардварный i2c? Все настройки можно выудить в даташите (ну или инете), поэтому, на этот раз не будет пространственных разъяснений, а сразу приступаем к делу.

Выносим наши функции в отдельный файл i2c.c:


#include <htc.h>
#include "i2c.h"

void I2CInit(void){         //Инициализация
TRISC3 = 1;                 //SDA и SCL
TRISC4 = 1;
SSPSTAT |= 0x80;
SSPCON = 0x28;              // Режим ведущего, clock = FOSC/(4 * (SSPADD + 1))
SSPADD = 0x28;              // 100Khz @ 4Mhz Fosc
}

void I2CStart(){
SEN = 1;                    // Стартовые условия
while(SEN);
}

void I2CStop(){             // Процедура окончания передачи
PEN = 1;                    // Включаем условия окончания передачи
while(PEN);
}

void I2CRestart(){          // Повторный старт
RSEN = 1;
while(RSEN);
}

void I2CAck(){              // Шлем Ack бит
ACKDT = 0;
ACKEN = 1;
while(ACKEN);
}

void I2CNak(){              // Шлем Nack бит
ACKDT = 1;
ACKEN = 1;
while(ACKEN);
}

void I2CWait(){            // Растяжка клока
while ( ( SSPCON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );
}

void I2CSend(unsigned char dat){     // Процедура отсылки данных
SSPBUF = dat;                        // Запихиваем наш байт в буфер
while(BF);                           // Ждем пока все данные выпихнутся из буфера
I2CWait();
}

unsigned char I2CRead(void){         // Процедура чтения
unsigned char temp;
RCEN = 1;                            // Разрешаем прием данных
while(!BF);                          // Ждем пока заполнится буффер
temp = SSPBUF;                       // Присваиваем нашей переменной значение из буфера
I2CWait();
return temp;
}

В заголовочном файле просто объявления функций:


void I2CInit(void);


void I2CStart();

void I2CStop();
void I2CRestart();


void I2CAck();

void I2CNak();

void I2CWait();

void I2CSend(unsigned char dat);

unsigned char I2CRead(void);

Дело сделано, идем дальше.

На борту есть часы реального времени DS1307, создадим функции общения с ними на базе, созданных только что команд для I2C. Но чтобы все было уже совсем красиво, вынесем все функции, которые были разработаны в прошлой статье о RTC. Привожу содержимое файла rtc.c:


#include <htc.h>
#include <stdio.h>
#include "usart.h"
#include "rtc.h"
#include "i2c.h"

unsigned char BCDconv (unsigned char source) {
 unsigned char temp_min=0;
 unsigned char temp_maj=0;

 temp_min = source&15;
 temp_maj = source >> 4;
 temp_maj *= 10;
 return temp_maj+temp_min;
}

unsigned char DCBconv (unsigned char source) {
 unsigned char temp_min=0;
 unsigned char temp_maj=0;

 temp_maj = source/10 ;
 temp_min = source - temp_maj*10;
 temp_maj <<= 4;
 return temp_maj+temp_min;
}

void SetMin(unsigned char minutes) {        //записываем значение в минуты
 I2CStart();
 I2CSend(0b11010000);
 I2CSend(0x01);
 I2CSend(DCBconv(minutes));
 I2CStop();
}

unsigned char ReadMin() {
 unsigned char temp = 0;
 I2CStart();                               //читаем значение минут
 I2CSend(0b11010000);
 I2CSend(0x01);
 I2CRestart();
 I2CSend(0b11010001);
 temp = I2CRead();
 I2CNak();
 I2CStop();
 return BCDconv(temp);
}

void SetHour(unsigned char hours) {        //записываем значение в часы
 I2CStart();
 I2CSend(0b11010000);
 I2CSend(0x02);
 I2CSend(DCBconv(hours));
 I2CStop();
}

unsigned char ReadHour() {
 unsigned char temp = 0;
 I2CStart();            //читаем значение часов
 I2CSend(0b11010000);
 I2CSend(0x02);
 I2CRestart();
 I2CSend(0b11010001);
 temp = I2CRead();
 I2CNak();
 I2CStop();
 return BCDconv(temp);
}

void SetSeconds(unsigned char seconds) {        //записываем значение в секунды
 I2CStart();
 I2CSend(0b11010000);
 I2CSend(0x00);
 I2CSend(DCBconv(seconds));
 I2CStop();
}

unsigned char ReadSeconds() {
 unsigned char temp = 0;
 I2CStart();            //читаем значение минут
 I2CSend(0b11010000);
 I2CSend(0x00);
 I2CRestart();
 I2CSend(0b11010001);
 temp = I2CRead();
 I2CNak();
 I2CStop();
 return BCDconv(temp);

}

void ShowTime() {
 printf("\fTime - %d :", ReadHour());
 printf(" %d :", ReadMin());
 printf(" %d ", ReadSeconds());
 printf("\r\n*********************");
 printf("\r\n Menu");
 printf("\r\n 1 - Reload time");
 printf("\r\n 2 - Setup time");
}

void SetupTime() {
 unsigned char state = 1;
 unsigned char temp_min = 0;
 unsigned char temp_maj = 0;
 unsigned char temp = 0;
 while(state){
              printf("\fEnter hours - ");
              temp_maj = getch();
              printf(" %c", temp_maj);
              temp_min = getch();
              printf("%c", temp_min);
              temp = getch();
                if (temp == 13) {
                                temp_maj -= 48;
                                temp_min -= 48;
                                temp = temp_maj*10 + temp_min;
                                if (temp < 24) {
                                                SetHour(temp);
                                                state = 0;
                                               }
                                }
               }
 state = 1;
 while(state){
              printf("\fEnter minutes - ");
              temp_maj = getch();
              printf(" %c", temp_maj);
              temp_min = getch();
              printf("%c", temp_min);
              temp = getch();
               if (temp == 13) {
                               temp_maj -= 48;
                               temp_min -= 48;
                               temp = temp_maj*10 + temp_min;
                               if (temp < 61) {
                                              SetMin(temp);
                                              state = 0;
                                              }
                               }
              }
 state = 1;
 while(state){
              printf("\fEnter seconds - ");
              temp_maj = getch();
              printf(" %c", temp_maj);
              temp_min = getch();
              printf("%c", temp_min);
              temp = getch();
               if (temp == 13) {
                               temp_maj -= 48;
                               temp_min -= 48;
                               temp = temp_maj*10 + temp_min;
                                if (temp < 61) {
                                               SetSeconds(temp);
                                               state = 0;
                                               }
                                }
             }
ShowTime();
}

Код расписывать подробно не буду, т.к. уже раньше в упомянутой статье это делал. Точно также по поводу уарта – там все подробно расписано. Также оставлена процедура вывода через терминал на комп, на всякий пожарный.

Что ж, осталось добить термометр 🙂

Опять же, у нас есть готовая база для общения с DS18b20. Поэтому берем и просто добавляем нужные вставки в код (его я еще раз приводить не буду, не вижу смысла).

Добиваем код главной функции:


void main()
{
unsigned char input;          //переменная для уарта
unsigned char temp;           //переменая для всякой фигни :)

TRISB = 0x00;
init_comms();                //готовим уарт
I2CInit();                   //готовим I2C
printTemp();                 //выкатываем сразу значение температуры

for (;;) {
         input = getch();
         switch (input) {
                         case 49 : ShowTime();    //если нажата 1 то в функция отображения в терминале
                                   break;
                         case 50 : SetupTime();   //если нажата 2 - то процедура установки часов через терминал
                                   break;
                         case 51 :                //если нажата 3 - то последовательно выбрасываем каждый тип времени, каждому типу присвоен свой уникальный цифровой индикатор
                                  GIE = 0;
                                  temp = ReadHour();
                                  if (temp <10) printf("\r\n1010%d", temp); else printf("\r\n101%d", temp);
                                  __delay_ms(20);
                                  temp = ReadMin();
                                  if (temp <10) printf("\r\n1020%d", temp); else printf("\r\n102%d", temp);
                                  __delay_ms(20);
                                  temp = ReadSeconds();
                                  if (temp <10) printf("\r\n1030%d", temp); else printf("\r\n103%d", temp);
                                  __delay_ms(20);
                                  break;
                        case 52 : printTemp();     //если нажата 4 - то выбрасываем значение температуры со своим уникальным идентификатором
                                  break;

}
}

}

Проект для mplab можно взять тут.

Все готово для тестирования:

В видео видно, что по нажатию на кнопки выкатываются значния с цифрами 100-200 в начале, я их добавил, чтобы было легко парсить данные при выводе на gui нашего интерфейса. Собственно о начале интерфейса, в качестве базы я взял проект из недавней статьи и немножко дополнил его.

Добавил пару кнопок в гуевину:

Как видите, добавилась група “Sensors”, куда выкатываем показания времени и температуры. В обработку прерывания поступающих на порт данных, добавились следующие строки:

uint temp = temp_data.toUInt();            //Заводим численную переменную для обработки
qDebug() << "temp/100 is: " << temp/100;   //Вывод в режиме отладки, на всякий случай
      switch (temp/100) {                  //здесь происходит рассортировка данных
                        case 101 : ui->lbHour->setText(QString::number(temp-((temp/100))*100));     //часы
                                   break;
                        case 102 : ui->lbMin->setText(QString::number(temp-((temp/100))*100));      //минуты
                                   break;
                        case 103 : ui->lbSec->setText(QString::number(temp-((temp/100))*100));      //секунды
                                   break;
                        case 201 : ui->lbSign->setText("+");
                                   ui->lbTemp->setText(QString::number(temp-((temp/100))*100));     //целое значение температуры
                                   break;
                        case 202 : ui->lbSign->setText("-");
                                   ui->lbTemp->setText(QString::number(temp-((temp/100))*100));     //целое значение температуры
                                   break;
                        case 203 : ui->lbTempDrob->setText(QString::number(temp-((temp/100))*100)); //дробное значение температуры
                                   break;
      }

Видео с тест-драйвом:

На видео видны прострелы в работе, связанные с хреново написанной процедурой обработки данных с компа. Но это я буду править попозже, пока останавливаюсь на этой базе и перехожу к следующей ступени проекта.

Усилитель. Шаг 1. Мозги.: 2 комментария

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.