Вопрос новичка про arduino mega 2560 и сегментный дисплей shd0032 со сдвиговым регистром
- Войдите на сайт для отправки комментариев
Чт, 13/08/2015 - 13:28
Недавно приобрёл плату, хочу попытаться смастерить спидометр. И был у меня дисплей shd0032 http://lib.chipdip.ru/042/DOC001042790.pdf . Два дня начитывался статей вроде "Подключение LCD к Arduino через сдвиговый регистр 74HC595" и перекапывал интернет. Дисплей заработал по этой схеме http://forum.homedistiller.ru/index.php?topic=106725.400 , но я никак не могу понять как вывести на дисплей желаемое число, пытался из кода в статье выдернуть всё что касается дисплея, но ничего не получилось. Может кому попадались статьи по подключению таких дисплеев, может для него какие библиотеки есть.
все просто: вам нужно знать соотвесвие битов(0..7) сегментам(a...g) вот пример для SPI - SCK(строб) и MOSI(данные):
#define NUM 4 //число цифр #define LATCH 5 // пин регистра const byte font[21] = //seven segment digits in bits { //PGFEDCBA сегменты B11000000, //0 B11111001, //1 B10100100, //2 B10110000, //3 B10011001, //4 B10010010, //5 B10000010, //6 B11111000, //7 B10000000, //8 B10010000, //9 0xFF, //space B10111111, //- B11110111, //_ B10001000, //A B10000011, //b B11000110, //C B10100001, //d B10000110, //E B10001110, //F B11000010, //G B10001001 //H }; byte digit(char c){ switch(c){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return font[c - '0']; case '-': return font[11]; case '_': return font[12]; case 'A': case 'a': return font[13]; case 'B': case 'b': return font[14]; case 'C': case 'c': return font[15]; case 'D': case 'd': return font[16]; case 'E': case 'e': return font[17]; case 'F': case 'f': return font[18]; case 'G': case 'g': return font[19]; case 'H': case 'h': return font[20]; case ' ': default: return font[10]; } } void Display(const char* str, int len){ for(int i=len;i>=0;i--){ SPI.transfer(digit(str[i])); } for(int i=len;i<NUM;i++){ SPI.transfer(digit(' ')); } digitalWrite(LATCH, LOW); digitalWrite(LATCH, HIGH); }Только сам код вывода. Использование: Display("1234",4);
Нашёл другой пример кода с этим дисплеем, но сколько не пытался понять и выдернуть необходимый код, покуда ничего не вышло, вечные ошибки
// Детектор нуля идет на 0-е прерывание (pin 2) // Прерывание от валкодера (B pin валкодера) на 1 прерывание (pin 3) // Второй пин валкодера (A) подключен к pin 4 // Управление симистором для фазового регулятора реализовано через pin 5 // Нажатие кнопки валкодера (S) подключено к pin 6 (низкий уровень при нажатии) // Вход ENABLE SND0032 дисплея подключен на pin 7 (активный уровень низкий) // Вход LATCH SND0032 дисплея подключен на pin 8 // Вход CLOCK SND0032 дисплея подключен на pin 9 // Вход SERIAL-IN SND0032 дисплея подключен на pin 10 // АЦП от трансформатора подключен на pin А0 #include <EEPROM.h> #define DEBUG // Режим отладки #define Serial1 Serial char m_version[]="v0.2"; #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif #define MAX_INDEX_INPUT 255 // Выравниваем по границе байта, тк увеличиваем считывание анального входа. (было 70) unsigned int data_adc[MAX_INDEX_INPUT+1]; unsigned int tic_adc[MAX_INDEX_INPUT+1]; volatile uint16_t deltaArray[16]; // массив для вычисления средней дельты отставания реального нуля напряжения volatile uint8_t deltaIndex=0; // текущий индекс массива средней дельты отставания реального нуля напряжения volatile uint16_t deltaZ; // Дельта в тиках прерывания нуля и начала возрастания напряжения. volatile unsigned int VolVal; // Считанное с АЦП значение напряжения volatile int MaxVolts, MaxVoltsOut; // Пиковое напряжение volatile unsigned char StateVolts=0; // Состояние вычисление среднеквадратичного //0 - надо вычислить - запускается старт, 1 - вычисляется, 2 - вычислено, 3 - идет обработка рузультата // 4 - Результат обработан, можно вычислять следующий unsigned int ticIndex=0; // Текущий индекс массива дла расчета среднеквадратичного // Serial #define RX_BUFFER_SIZE 70 #define TX_BUFFER_SIZE 70 char tbuffer[TX_BUFFER_SIZE]; char FlUsart=0; // Признак того, что надо вывести информацию на СОМ-порт char RBuffer[RX_BUFFER_SIZE]; int PosRx = 0; #define PIN_TRIAC 5 // Управление симистором реализовано через PIN 5 #define PIN_VOLC_B 4 // Определение пина B валкодера #define PIN_VOLC_S 6 // Определение пина S валкодера #define D_IN 10 //Определение бита IN #define D_CLOCK 9 //Определение бита CLOCK #define D_LATCH 8 //Определение бита LATCH #define D_ENABLE 7 //Определение бита ENABLE #define BYTES 4 //Количество разрядов unsigned char chars[BYTES] ={0,0,0,0}; //Массив разрядов #define SYMBOLS_ARRAY_LENGTH 12 //Длина массива символов unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора 252, // 0 96, // 1 218, // 2 242, // 3 102, // 4 182, // 5 190, // 6 224, // 7 254, // 8 246, // 9 1, // . 2 // - }; char CountKeys; // Переменная для защиты от дребезга char reDisplayData; // Флаг обновления информации на дисплее/ // Переменные второго счетчика volatile int Counter1=0; volatile unsigned long Sec, LSec; // Текущее состояние времени с начала процесса (не беззнаковое, потому, что есть проверки на <0); volatile unsigned long pChangeSec; // Временная отметка последних изменений в параметрах char paramChanged = 0; // Флаг, что требуется перезаписать eeprom unsigned long TekPower; unsigned int OpenTriacArray[16]; unsigned int OpenTriacIndex=0; unsigned int TimeOpenTriac=0; unsigned int TicSqNapr; // Количество тиков таймера от нуля до нуля, на основании которого рассчитано среднеквадратичное // Установка мощности char PowerEnabled = 1; // Флаг включения нагрузки unsigned int Power=2000; // Номинальная мощность ТЭНов unsigned int UstPower=1000; // Нужно получить мощность от регулятора (текущая мощность) /* * Режимы работы */ #define MODE_UST_POWER 0 // Режим отображения/установки текущей мощности #define MIN_MODE MODE_UST_POWER // < Минимальный режим #define MODE_DESP_MAXU 1 // Режим отображения максимального напряжения #define MODE_MAX_POWER 2 // Режим отображения/установки максимальной мощности #define MAX_MODE MODE_MAX_POWER // < Максимальный режим unsigned char StateMachine = MIN_MODE; #define PR_REWRITE_EEPROM 19 // Константа, которая содержит признак необходимости перезаписи энергонезависимой памяти (1-254). // При запуске программы, значение 0-го байта ЕЕПРОМ сравнивается с этим знначением, // и если они не совпадают, тогда энергонезависимая памиять переписывается текущими значениями переменных // То есть для перезаписи контанты при первом запуске, ее значение надо поменять unsigned int ee_addr; // Переменная для хранения текущего адреса чтения/записи в EEPROM unsigned char eeReadChar() { unsigned char lByte = EEPROM.read(ee_addr); ee_addr++; return lByte; } void eeWriteChar(char p_value) { if (EEPROM.read(ee_addr)!=p_value) EEPROM.write(ee_addr, p_value); ee_addr++; } void eeWriteInt(int p_value) { unsigned char lByte = (unsigned char) (p_value); unsigned char hByte =(unsigned char) (p_value >> 8); // Для экономии ресурса памяти, сначала проверяем ее содержимое и запись производим только если значение отличается. eeWriteChar(lByte); eeWriteChar(hByte); } //This function will read a 2 byte integer from the eeprom at the specified address and address + 1 unsigned int eeReadInt() { unsigned char lByte = eeReadChar(); unsigned char hByte = eeReadChar(); return (unsigned int) (lByte) | (unsigned int) (hByte << 8); } // Сохранение всех полезных настроек в eeprom void writeEEPROM() { int i; ee_addr=0; eeWriteChar(PR_REWRITE_EEPROM); eeWriteInt(Power); eeWriteInt(UstPower); eeWriteChar(StateMachine); } // Чтение настроек из eeprom void readEEPROM() { int i; ee_addr=0; eeReadChar(); // Пропуск первого байта Power=eeReadInt(); UstPower=eeReadInt(); StateMachine = eeReadChar(); } //Отправить 1 байт в SERIAL_IN дисплея void push_byte(unsigned char _byte) { unsigned char j; for(j=0;j<8;j++){ digitalWrite(D_CLOCK, LOW); digitalWrite(D_IN, 1 & _byte); _byte = _byte>>1; digitalWrite(D_CLOCK, HIGH); } } //Показать текущие значения в массиве разрядов void dispData(void) { unsigned char i; unsigned char dot = (Sec & 1); switch (StateMachine) { case MODE_UST_POWER: // Режим отображения текущей мощности i = UstPower/1000; if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = Symbols[i]+dot; i = (UstPower%1000)/100; if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i]; i = (UstPower%100)/10; if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i]; i = (UstPower%10); if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i]; break; case MODE_DESP_MAXU: // Режим отображения максимального напряжения if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = 16; // Это код символа нижнего подчеркивания. i = (MaxVoltsOut%1000)/100; if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i]; i = (MaxVoltsOut%100)/10; if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i]; i = (MaxVoltsOut%10); if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i]; break; case MODE_MAX_POWER: // Режим отображения максимальной мощности i = Power/1000; if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = Symbols[i]+dot; i = (Power%1000)/100; if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i]+dot; i = (Power%100)/10; if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i]+dot; i = (Power%10); if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i]+dot; break; } for (i=0;i<BYTES;i++) { push_byte(chars[i]); } digitalWrite(D_LATCH, HIGH); digitalWrite(D_LATCH, LOW); reDisplayData = 0; } void setup() { uint8_t analog_reference = DEFAULT; Serial1.begin(9600); // Настройка ADC sbi(ADCSRA,ADPS2); cbi(ADCSRA,ADPS1); cbi(ADCSRA,ADPS0); ADMUX = (analog_reference << 6) | (0 & 0x07); sbi(ADCSRA,ACIE); sbi(ADCSRA,ADSC); sbi(ADCSRA,ADATE); // Читаем ранее сохраненные значения из энергонезависимой памяти. if (EEPROM.read(0)!=PR_REWRITE_EEPROM) { writeEEPROM(); } pinMode(PIN_TRIAC,OUTPUT); digitalWrite(PIN_TRIAC,LOW); pinMode(PIN_VOLC_B, INPUT); pinMode(PIN_VOLC_S, INPUT); digitalWrite(PIN_VOLC_S,HIGH); pinMode(D_IN, OUTPUT); pinMode(D_CLOCK, OUTPUT); pinMode(D_LATCH, OUTPUT); pinMode(D_ENABLE, OUTPUT); attachInterrupt(1, VOLC_ISR, RISING); attachInterrupt(0, zero_crosss_int, RISING); // Устанавливаем на 0-е прерывание функцию (это pin 2) обработку от детектора нулюя сетевого напряжения // Таймер 2 используется для подсчета числа секунд и запуска расчета чреднеквадратичного напряжения на входе // Прерываение по этому таймеру будет вызываться 125 раз в секунду. TCCR2A = (1<<WGM21); // Режим CTC (сброс по совпадению) TCCR2A=0x00; TCCR2B=0x07; // TCNT2=0x00; OCR2A=0x7D; OCR2B=0x00; TIMSK2 = (1<<OCIE2A); // Разрешить прерывание по совпадению // Таймер 1 - управление открытием симистора TCCR1A=0x00; TCCR1B=0x03; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x02; OCR1BH=0x00; OCR1BL=0x00; TIMSK1 = (1<<OCIE1A); // Разрешить прерывание по совпадению readEEPROM(); // Читаем все из eeprom // Обновляем данные на дисплее и включаем его dispData(); digitalWrite(D_ENABLE, LOW); StateVolts=0; sei(); // Глобально разрешить прерывания } /** * Функция обработки прерывания от линии B валкодера * В зависимости от состояния линии A валкодера, определяем направление вращения. */ void VOLC_ISR() { char v = digitalRead(PIN_VOLC_B); switch (StateMachine) { case MODE_UST_POWER: // Установка текущей мощности if (v) { UstPower++; if (UstPower > Power) UstPower=Power; } else { UstPower--; if (UstPower < 0) UstPower=0; } paramChanged = 1; // Отмечаем, что требуется запись в eeprom pChangeSec = Sec; // Отметка времени последнего изменения reDisplayData++; break; case MODE_MAX_POWER: // Установка максимальной мощности if (v) { Power++; if (UstPower > Power) UstPower=Power; } else { Power--; if (Power < 0) Power=0; } paramChanged = 1; // Отмечаем, что требуется запись в eeprom pChangeSec = Sec; // Отметка времени последнего изменения reDisplayData++; break; } } // Прерывание управления симистором. ISR(TIMER1_COMPA_vect) { // Включаем симистор if (UstPower>0 && PowerEnabled) digitalWrite(PIN_TRIAC,HIGH); } ISR(ADC_vect) { static unsigned int prev_tic = 0; static char pad = 0; unsigned int tic; VolVal = ADCL|(ADCH << 8); // Пропускаем каждые 4 отсчета if (++pad<5) return; pad = 0; tic = (uint16_t) TCNT1L | (uint16_t) (TCNT1H<<8); // Расчитывааем текущее значение таймера // Обходим сброс таймера по прерыванию нуля if (prev_tic < tic) prev_tic = tic; else tic = prev_tic + tic; if (VolVal < 2) { // Если дана команда рассчитать среднеквадратичное, или среднеквадратичное рассчитывается, но 0 встретился раньше, чем положено, то запускаем расчет заново. if (StateVolts==0 || (StateVolts==1 && ticIndex<=20)) { ticIndex=0; StateVolts=1; tic=0; MaxVolts=0; prev_tic = 0; } } // Если ноль встретился и процесс расчета идет, тогда ставим флаг окончания процесса. if (StateVolts==1 && ticIndex > 80) { if (MaxVolts>100) { // Если максимальное напряжение менее 100 вольт - дропаем результаты и начинаем заного data_adc[ticIndex] = VolVal; tic_adc[ticIndex] = tic; ticIndex++; StateVolts=2; MaxVoltsOut=MaxVolts; prev_tic = 0; } else { StateVolts = 0; } } // Если процесси идет, тогда просто накапливаем значения измерений if (StateVolts==1) { data_adc[ticIndex]=VolVal; tic_adc[ticIndex]=tic; if (ticIndex==1) { // при первом ненулевом результате фиксируем дельту deltaArray[deltaIndex++] = tic; if (deltaIndex>=16) deltaIndex=0; } ticIndex++; if (VolVal>MaxVolts) MaxVolts=VolVal; } // Если превышен размер массива, то считаем эту выборку несостоятельной (Вдруг глюк АЦП или помеха в сети), начинаем снова. if (ticIndex>=MAX_INDEX_INPUT) StateVolts=0; } // 2 Прерывание вызывается 125 раз в секунду ISR(TIMER2_COMPA_vect) { if (Counter1 % 20 ==0) { if (StateVolts==4) { StateVolts=0; // Раз в 1/5 секунды запускаем измерение среднеквадратичного (в дальнейшем можно будет настроить, // чтобы оно делалось так часто, насколько позволяют возможности контроллера). } } // Следим за защитой от дребезга клавиш if (Counter1 % 20 ==0) { if (CountKeys>0) CountKeys--; } Counter1++; if (Counter1>=125) { reDisplayData++; // обновляем дисплей Sec++; Counter1=0; } // Сброс таймера TCNT2=0x00; } // Функция вызывается по прерыванию нуля от нуля сети 220 вольт void zero_crosss_int() // function to be fired at the zero crossing to dim the light { unsigned int TimeOpenTriacFact=0; // Сбрасываем таймер. TCNT1H=0x00; TCNT1L=0x00; if (UstPower>=Power) { // Если мощность больше или равна номинальной, включаем триак всегда, при этом прерывание на закрытие никогда не выполнится. TimeOpenTriac=5000; } else { digitalWrite(PIN_TRIAC,LOW); } // время открытие + ошибка прохождения нуля TimeOpenTriacFact = TimeOpenTriac + deltaZ; // В прерывании нуля настраиваем, чтобы прерывание на 1-м таймере выдалось через то время, которое мы ранее рассчитали. OCR1AH=(char)(TimeOpenTriacFact>>8); OCR1AL=(char)TimeOpenTriacFact; } void loop() { char *Command = NULL, *ptr, *ptr2; unsigned int Val; int i,j, s; unsigned long SqNapr=0,SqNaprPrev=0,FindPower; unsigned int TimeOpenTriac1=0; char s_rx; // Опрос нажатия кнопки валкодера (S) if (!CountKeys) { if (LOW == digitalRead(PIN_VOLC_S)) { StateMachine++; if (StateMachine>MAX_MODE) StateMachine = MIN_MODE; paramChanged = 1; // Отмечаем, что требуется запись в eeprom pChangeSec = Sec; // Отметка времени последнего изменения reDisplayData++; CountKeys=2; // защита от дребезга } } // Проверка необходимости записа изменений в eeprom, если небыло дальнейших изменений // в течении 5 секунд. Это сделано, чтобы валкодером не протереть в ней дырку if (paramChanged>0 && pChangeSec>0 && Sec > pChangeSec+5) { writeEEPROM(); paramChanged = 0; pChangeSec = 0; } // Обработка принудительного обновления информации на дисплее if (reDisplayData) dispData(); if (LSec != Sec) { LSec = Sec; sprintf(tbuffer,"DATA %u %u %u %u %u", PowerEnabled, Power, UstPower, Sec, MaxVoltsOut*707/1000); Serial1.println(tbuffer); } if (Serial.available() > 0) { //если есть доступные данные считываем байт s_rx=Serial.read(); if (s_rx == '\r') { RBuffer[PosRx]=0; PosRx=0; #ifdef DEBUG /* Отладка - возврат строки */ Serial.print("dbg: "); Serial.println(RBuffer); #endif /* DEBUG */ // передаем данные в следующем формате: // сколькоприбавитьсекунд,Темпереатура1,Температура2, Температура3 ptr = RBuffer; ptr2=strchr(ptr,','); if (ptr2) { *ptr2++ = 0; Command = ptr; Val = atoi(ptr2); if(!strcmp(Command,"MAXP")) { /* Установка номинальной мощности тэнов */ Power=Val; sprintf(tbuffer,"MAXP: %u", Power); FlUsart++; } else if(!strcmp(Command,"ENABLE")) { /* Включение иил выключение напряжения на нагрузке */ PowerEnabled = Val; sprintf(tbuffer,"ENABLE: %u", Val); FlUsart++; } else if(!strcmp(Command,"POWER")) { /* Установка мощности на нагрузке */ UstPower = Val; if (UstPower > Power) UstPower = Power; sprintf(tbuffer,"POWER: %u", UstPower); FlUsart++; } } } else { /* не символ возврата коретки - помещаем в буфер */ if (PosRx>=RX_BUFFER_SIZE) PosRx=0; RBuffer[PosRx++]=s_rx; } } // Окончание анализа асинхронного порта if (FlUsart>0) { // Признак того, что надо выдать информацию в ком-порт (есл ее вдруг невозможно сразу выдать, например, из прерывания). Serial1.println(tbuffer); FlUsart=0; } // Начало расчета среднеквадратичного if (StateVolts==2){ // Расчитываем среднее смещение прерывания нуля и реального нуля uint16_t deltaSum=0; for(i=0;i<16;i++) deltaSum += deltaArray[i]; deltaZ = deltaSum >> 4; // деление на 16 StateVolts=3; #ifdef DEBUG Serial1.println("BEGIN SQNAPR"); #endif /* DEBUG */ // поскольку тут идет преобразование и умножение 32 разрядных чисел, делаем это по необходимости, а не каждый раз TekPower=(unsigned long) UstPower*220*220/ Power; SqNapr= SqNaprPrev= TimeOpenTriac1=0; FindPower=TekPower*ticIndex; if (UstPower < Power) { for(i=ticIndex-1; i>=0; i--) { SqNapr += (unsigned long) data_adc[i]* (unsigned long) data_adc[i]; if (SqNapr>=FindPower) { // Рассчитали среднеквадратичное OpenTriacArray[OpenTriacIndex++] = tic_adc[i] + (SqNapr-FindPower)*(tic_adc[i+1]-tic_adc[i])/(SqNapr-SqNaprPrev); if (OpenTriacIndex>=16) OpenTriacIndex=0; for(j=0; j<16; j++) TimeOpenTriac1 += OpenTriacArray[j]; TimeOpenTriac = TimeOpenTriac1 >>4; // деление на 16 // Присвоили текущие значения TicSqNapr=tic_adc[ticIndex-1]; // Количство тиков таймера, на основании которого расчитано среденеквадратичное. break; } SqNaprPrev=SqNapr; } } static int jj=0; #ifdef DEBUG Serial1.println("VOLTS"); sprintf(tbuffer,"Zr=%4u %u",TimeOpenTriac,OpenTriacIndex); Serial1.println(tbuffer); sprintf(tbuffer,"Pm/P=%u %u", Power,(long) UstPower); Serial1.println(tbuffer); sprintf(tbuffer,"Zr=%4u U=%3u",TimeOpenTriac,(long) MaxVoltsOut*707/1000); Serial1.println(tbuffer); sprintf(tbuffer,"COUNT=%u",ticIndex); Serial1.println(tbuffer); /* for(i=0;i<ticIndex;i++){ // Данные для построения графика напряжения sprintf(tbuffer,"$t[%u][", jj); Serial1.print(tbuffer); sprintf(tbuffer,"%u]=",tic_adc[i]); Serial1.print(tbuffer); Serial1.print(data_adc[i]); Serial1.println(";"); } jj++; */ sprintf(tbuffer,"deltaZ=%u %u", deltaZ, TicSqNapr); Serial1.println(tbuffer); Serial1.println("ENDVOLTS"); #endif /* DEBUG */ StateVolts=4; } // окончание расчета среднеквадратичного }