Подключение LCD 20X4
- Войдите на сайт для отправки комментариев
Сб, 13/10/2012 - 15:52
На дисплей lcd 16x2 выводится меню из шести строк для управления мотором. Всё работает. Нужно подключить lcd 20x4. Инициировал. Написал заставку - выводится на все четыре строчки . Меню выводится только на две строки. В чём проблема ?
1. Скорее всего меню писалось для дисплея 16х2, а не 20х4.
2. Проблема в том, что здесь нет экстрасенсов и никто не видит что у вас за код.
Вставка программного кода в тему/комментарий
Вот часть кода, где есть дисплей
#include <avr/pgmspace.h> #include <EEPROM.h> #include <LiquidCrystal.h> #include "MsTimer2.h" #include "TimerOne.h" #include "merlin_mount.h"; #define FIRMWARE_VERSION 900 // motor PWM #define MOTOR0_P 5 #define MOTOR1_P 6 #define MOTOR0_DIR 15 #define MOTOR1_DIR 16 // camera pins #define CAMERA_PIN 13 #define FOCUS_PIN 12 #define MAX_MOTORS 2 //#define MP_PERIOD 30 #define MP_PERIOD 30 /* User Interface Values */ // lcd pins #define LCD_RS 17 #define LCD_EN 18 #define LCD_D4 4 #define LCD_D5 11 #define LCD_D6 9 #define LCD_D7 8 // which input is our button #define BUT_PIN 14 // lcd backlight pin #define LCD_BKL 7 // max # of LCD characters (including newline) #define MAX_LCD_STR 17 // how many buttons dow we have? #define NUM_BUTTONS 5 // button return values #define BUT0 1 #define BUT1 2 #define BUT2 3 #define BUT3 4 #define BUT4 5 // which buttons? #define BUT_UP BUT4 #define BUT_DN BUT3 #define BUT_CT BUT0 #define BUT_LT BUT2 #define BUT_RT BUT1 // analog button read values #define BUT0_VAL 70 #define BUT1_VAL 250 #define BUT2_VAL 450 #define BUT3_VAL 655 //#define BUT3_VAL 540 #define BUT4_VAL 830 // button variance range #define BUT_THRESH 60 // how many ms does a button have // to be held before triggering another // action? (for scrolling, etc.) #define HOLD_BUT_MS 200 // how much to increment for each cycle the button is held? #define HOLD_BUT_VALINC 10 // ALT input debouncing time #define ALT_TRIG_THRESH 250 // menu strings prog_char menu_1[] PROGMEM = "Manual Move"; prog_char menu_2[] PROGMEM = "Motor Rot-Pan"; prog_char menu_3[] PROGMEM = "Motor Lin"; prog_char menu_4[] PROGMEM = "Camera"; prog_char menu_5[] PROGMEM = "Settings"; prog_char menu_6[] PROGMEM = "Scope"; prog_char manual_menu_1[] PROGMEM = "Motor Rot-Pan"; prog_char manual_menu_2[] PROGMEM = "Motor Lin"; prog_char manual_menu_3[] PROGMEM = "Scope"; prog_char axis_menu_1[] PROGMEM = "Ramp Shots"; prog_char axis_menu_2[] PROGMEM = "RPM"; prog_char axis_menu_3[] PROGMEM = "Fixed SMS"; prog_char axis_menu_4[] PROGMEM = "Angle"; prog_char axis_menu_5[] PROGMEM = "Calibrate"; prog_char axis_menu_6[] PROGMEM = "Slow Mode IPM"; prog_char axis_menu_7[] PROGMEM = "Dist. per Rev"; prog_char axis_menu_8[] PROGMEM = "Min Pulse"; prog_char axis_menu_9[] PROGMEM = "Axis Type"; prog_char axis_menu_10[] PROGMEM = "Lead In"; prog_char axis_menu_11[] PROGMEM = "Lead Out"; prog_char axis_menu_12[] PROGMEM = "Cal. Constant"; prog_char camera_menu_1[] PROGMEM = "Interval sec"; prog_char camera_menu_2[] PROGMEM = "Max Shots"; prog_char camera_menu_3[] PROGMEM = "Exp. Time ms"; prog_char camera_menu_4[] PROGMEM = "Exp. Delay ms"; prog_char camera_menu_5[] PROGMEM = "Focus Tap ms"; prog_char camera_menu_6[] PROGMEM = "Shutter+Focus"; prog_char camera_menu_7[] PROGMEM = "Repeat"; prog_char camera_menu_8[] PROGMEM = "Repeat Delay"; prog_char set_menu_1[] PROGMEM = "Motor Disp"; prog_char set_menu_2[] PROGMEM = "Motor Sl.Mod"; prog_char set_menu_3[] PROGMEM = "Backlight"; prog_char set_menu_4[] PROGMEM = "AutoDim (sec)"; prog_char set_menu_5[] PROGMEM = "Blank LCD"; prog_char set_menu_6[] PROGMEM = "I/O 1"; prog_char set_menu_7[] PROGMEM = "I/O 2"; prog_char set_menu_8[] PROGMEM = "Metric Disp."; prog_char set_menu_9[] PROGMEM = "Reset Mem"; prog_char set_menu_10[] PROGMEM = "Scope"; prog_char set_menu_11[] PROGMEM = "Cal. Spd Low"; prog_char set_menu_12[] PROGMEM = "Cal. Spd Hi"; prog_char set_menu_13[] PROGMEM = "AltOut Pre ms"; prog_char set_menu_14[] PROGMEM = "AltOut Post ms"; prog_char scope_menu_1[] PROGMEM = "Pan Man. Spd."; prog_char scope_menu_2[] PROGMEM = "Tilt Man. Spd."; // menu organization PROGMEM const char *menu_str[] = { menu_1, menu_2, menu_3, menu_4, menu_5, menu_6 }; PROGMEM const char *man_str[] = { manual_menu_1, manual_menu_2, manual_menu_3 }; PROGMEM const char *axis0_str[] = { axis_menu_1, axis_menu_10, axis_menu_11, axis_menu_2, axis_menu_3, axis_menu_4, axis_menu_5, axis_menu_12, axis_menu_6, axis_menu_7, axis_menu_8, axis_menu_9 }; PROGMEM const char *axis1_str[] = { axis_menu_1, axis_menu_10, axis_menu_11, axis_menu_2, axis_menu_3, axis_menu_4, axis_menu_5, axis_menu_12, axis_menu_6, axis_menu_7, axis_menu_8, axis_menu_9 }; PROGMEM const char *cam_str[] = { camera_menu_1, camera_menu_2, camera_menu_3, camera_menu_4, camera_menu_5, camera_menu_6, camera_menu_7, camera_menu_8 }; PROGMEM const char *set_str[] = { set_menu_1, set_menu_2, set_menu_3, set_menu_4, set_menu_5, set_menu_6, set_menu_7, set_menu_8, set_menu_9, set_menu_10, set_menu_11, set_menu_12, set_menu_13, set_menu_14 }; PROGMEM const char *scope_str[] = { scope_menu_1, scope_menu_2 }; // max number of inputs for each menu (in order listed above, starting w/ 0) byte max_menu[7] = {5,2,10,10,7,13,1}; // support a history of menus visited up to 5 levels deep byte hist_menu[5] = {0,0,0,0,0}; char lcd_buf[MAX_LCD_STR]; // what is our currently selected menu? // what is our current position? byte cur_menu = 0; byte cur_pos = 0; byte cur_pos_sel = 0; // which input value position are we in? byte cur_inp_pos = 0; // input buffers unsigned long cur_inp_long = 0; float cur_inp_float = 0.0; boolean cur_inp_bool = false; // which input are we on, if on // the main screen. byte main_scr_input = 0; // last read button (analog) value int last_but_rd = 1013; // flags for each button // use indivial bits to indicate // whether a given button was pressed. byte button_pressed = 0; // input value multiplier unsigned int inp_val_mult = 1; // how long has a button been held for? unsigned long hold_but_tm = 0; // when was ui last updated on home scr? unsigned long ui_update_tm = 0; // lcd dim control byte cur_bkl = 255; boolean blank_lcd = false; // for dimming lcd unsigned int lcd_dim_tm = 5; unsigned long input_last_tm = 0; // show cm instead of inch? boolean ui_is_metric = false; // floats are input in tenths? boolean ui_float_tenths = false; /* user interface control flags B0 = update display B1 = currently in setup menu B2 = in value entry B3 = have drawn initial value in value entry B4 = have used decimal in current value B5 = in manual mode B6 = lcd bkl on B7 = in calibrate mode */ byte ui_ctrl_flags = B00000000; /* calibration screen flags B0 = Currently calibrating B1 = Done Calibrating */ byte ui_cal_scrn_flags = 0; // whether to show ipm (true) or pct (false) boolean ui_motor_display = true; /* input type flags B0 = input value is a float B1 = input is a bool (on/off) value B2 = input is a bool (up/dn) value B3 = input is a bool (lt/rt) value B4 = input is a bool (ipm/pct) value B5 = input is a bool (pulse/sms) value B6 = input is a bool (rotary/linear) value B7 = input is list (0,45,90) value */ byte ui_type_flags = 0; /* input type flags vector #2 B0 = input value is list (Disable/Start/Stop) B1 = input value is forced metric */ byte ui_type_flags2 = 0; /* run status flags B0 = running B1 = camera currently engaged B2 = camera cycle complete B3 = motors currently running B4 = external trigger engaged */ volatile byte run_status = 0; /* external intervalometer B0 = I/O 1 is external intervalometer B1 = I/O 2 is external intervalometer B2 = interval OK to fire */ byte external_interval = 0; /* external trigger via alt i/o pins B0 = I/O 1 external enabled (before) B1 = I/O 2 external enabled (before) B2 = I/O 1 external enabled (after) B3 = I/O 2 external enabled (after) */ byte external_trigger = 0; // trigger delays unsigned long ext_trig_pre_delay = 0; unsigned long ext_trig_pst_delay = 0; // motor slow mode is pulse (true) or sms (false) boolean motor_sl_mod = true; // camera exposure time unsigned long exp_tm = 100; // tap focus before exposing unsigned int focus_tap_tm = 0; // delay after exposing (mS) unsigned int post_delay_tm = 100; // brign focus pin high w/ shutter boolean focus_shutter = true; // intervalometer time (seconds) float cam_interval = 1.0; // max shots unsigned int cam_max = 0; // camera repeat shots byte cam_repeat = 0; // delay between camera repeat cycles unsigned int cam_rpt_dly = 250; byte pre_focus_clear = 0; unsigned long cam_last_tm = 0; // currently selected motor byte cur_motor = 0; // set speed for the current motor unsigned int m_speeds[2] = {0,0}; // currently set speed (for altering motor speed) unsigned int mcur_spds[2] = {0,0}; // prev direction for motor byte m_wasdir[2] = {0,0}; // distance (i) per revolution float m_diarev[2] = {3.53, 3.53}; // motor RPMs float m_rpm[2] = { 8.75, 8.75 }; // calculated max ipm float max_ipm[2] = {m_diarev[0] * m_rpm[0], m_diarev[1] * m_rpm[1]}; // user-configurable min ipm float min_ipm[2] = {20.0, 20.0}; // minimumspeed (min ipm->255 scale value) byte min_spd[2] = { (min_ipm[0] / max_ipm[0]) * 255, (min_ipm[1] / max_ipm[1]) * 255 }; // minimum pulse cycles per motor byte m_min_pulse[2] = { 125, 125 }; // calibration points byte motor_spd_cal[2] = {1,40}; // linear or rotation type? byte m_type[2] = {0,0}; // fixed sms? byte m_smsfx[2] = {1,1}; // maximum sms distance unsigned int m_maxsms[2] = { max_ipm[0] * 100, max_ipm[1] * 100}; // for timer1 pulsing mode control boolean timer_used = false; volatile bool timer_engaged = false; volatile bool motor_engaged = false; volatile byte motor_ran = 0; // motor calibration float m_cal_constant[2] = {0.69, 0.69}; float m_cal_array[2][3][3][2] = { { { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} }, { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} }, { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} } }, { { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} }, { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} }, { {0.61914329,0.61914329},{1.0,1.0},{2.01133251,2.11453032} } } }; byte m_cur_cal = 0; byte m_angle[2] = {0,0}; boolean m_cal_done = false; // ramping data byte m_ramp_set[2] = {0,0}; float m_ramp_shift[2] = {0.0,0.0}; byte m_ramp_mod[2] = {0,0}; // lead-ins for axis movement unsigned int m_lead_in[2] = {0,0}; unsigned int m_lead_out[2] = {0,0}; // for controlling pulsing and sms movement unsigned long on_pct[2] = {0,0}; unsigned long off_pct[2] = {0,0}; unsigned int m_sms_tm[2] = {0,0}; // shots fired unsigned long shots = 0; // function types for alt inputs... /* 0 = disabled 1 = start 2 = stop */ byte input_type[2] = {0,0}; unsigned long input_trig_last = 0; boolean merlin_enabled = false; byte merlin_dir[2] = {0,0}; byte merlin_wasdir[2] = {0,0}; float merlin_speeds[2] = {0.0,0.0}; float merlin_man_spd[2] = {1440.0,1440.0}; /* 0 = axis 0 currently running free (continuous) 1 = axis 1 currently running free 2 = in merlin manual control 3 = displaying merlin config screen */ byte merlin_flags = 0; // initialize LCD object LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7); void setup() { pinMode(CAMERA_PIN, OUTPUT); pinMode(FOCUS_PIN, OUTPUT); pinMode(MOTOR0_P, OUTPUT); pinMode(MOTOR1_P, OUTPUT); pinMode(MOTOR1_DIR, OUTPUT); pinMode(MOTOR0_DIR, OUTPUT); Serial.begin(9600); init_user_interface(); delay(1000); // check firmware version stored in eeprom // will cause eeprom_saved() to return false // if version stored in eeprom does not match // firmware version. This automatically clears // saved memory after a new firmware load - // saving lots of support questions =) eeprom_versioning(); // did we previously save settings to eeprom? if( eeprom_saved() ) { // restore saved memory restore_eeprom_memory(); } else { // when memory has been cleared, or nothing has been // saved, make sure eeprom contains default values write_all_eeprom_memory(); } show_home(); /* for( byte i = 0; i <= 2; i++) { Serial.print(i, DEC); Serial.print(":"); for ( byte x = 0; x < 2; x++ ) { Serial.print(m_cal_array[0][0][i][x], 8); Serial.print(":"); } Serial.println(""); } */ } void loop() { if( run_status & B10000000 ) { // program is running main_loop_handler(); } // end if running else { // not running, not in manual, but merlin motors are running? if( ! ( ui_ctrl_flags & B00000100 ) && merlin_flags & B11000000 ) { merlin_stop(0); merlin_stop(1); } } // always check the UI for input or // updates check_user_interface(); } void main_loop_handler() { static boolean camera_fired = false; static boolean motors_clear = false; static boolean ok_stop = false; static boolean in_sms_cycle = false; static boolean do_fire = false; static boolean ext_trip = false; static byte cam_repeated = 0; if( cam_max > 0 && shots >= cam_max && ( ok_stop || (m_speeds[0] <= 0.0 && m_speeds[1] <= 0.0) || motor_sl_mod ) ) { // stop program if max shots exceeded, and complete cycle completed // if in interleave, ignore ocmplete cycle if in pulse ok_stop = false; stop_executing(); // interrupt further processing } else if( pre_focus_clear == 2 ) { // allow 100ms for focus line to settle before allowing any further MsTimer2::set(100, clear_cam_focus); MsTimer2::start(); pre_focus_clear = 3; } else if( motor_sl_mod && (( m_speeds[0] > 0 && m_speeds[0] < min_spd[0] ) || ( m_speeds[1] > 0 && m_speeds[1] < min_spd[1] ) ) ) { // if pulse mode is on and at least // one motor needs to be pulsed... motor_run_pulsing(); } // run merlin in continuous mode if needed if( motor_sl_mod && merlin_enabled ) { merlin_run_cont(); ok_stop = true; // allow max shots stop when // in continuous mode } // we need to deterime if we can shoot the camera // by seeing if it is currently being fired, or // is blocked in some way. After making sure we're // not blocked, we check to see if its time to fire the // camera if( motor_engaged ) { // motor currently moving // do not fire camera until motors are // done - we get caught up in here until the // motors have stopped moving if( motor_ran == 1 ) { // check to see if motor #2 needs to be run... if( in_sms_cycle == false && m_sms_tm[0] > 0 && m_sms_tm[1] > 0 ) { in_sms_cycle = true; // motor #2 remains to be run MsTimer2::set(m_sms_tm[1], stop_motor_sms); run_motor_sms(1); MsTimer2::start(); } else if(in_sms_cycle == false) { // no motors remain motor_engaged = false; ok_stop = true; } } else if ( motor_ran > 0 ) { // all of our motors have run one // cycle, let the camera fire motor_engaged = false; ok_stop = true; in_sms_cycle = false; } } // end if motor_engaged else if( run_status & B01001000 || pre_focus_clear == 3 ) { // currently firing the camera, focus, or triggering an external // control line // do nothing ; } else if( run_status & B00100000 ) { // camera cycle completed // clear exposure cycle complete flag run_status &= B11011111; if( camera_fired == true ) { // the shot just fired camera_fired = false; shots++; // for ramping motor speeds // we change speed in ramps after shots... motor_execute_ramp_changes(); // check to see if a post-exposure delay is needed if( post_delay_tm > 0 ) { // we block anything from happening while in the // post-exposure cycle by pretending to be an // exposure run_status |= B01000000; MsTimer2::set(post_delay_tm, camera_clear); MsTimer2::start(); motors_clear = false; ok_stop = false; } else { // no post-exp delay, is the external trigger to fire? if( external_trigger & B00110000 && ext_trig_pst_delay > 0 ) alt_ext_trigger_engage(false); //no post-exposure delay, motors can run motors_clear = true; } } else { // this was a post-exposure delay cycle completing, not // an actual shot // is the external trigger to fire? if( external_trigger & B00110000 && ext_trig_pst_delay > 0 ) alt_ext_trigger_engage(false); // we can set the motors clear to move now motors_clear = true; } // is the merlin head set to move? if( motors_clear == true && merlin_enabled && ! motor_sl_mod ) { // send merlin head to move sms distances (if desired) if( merlin_speeds[0] > 0 ) { merlin_send_angle(0, merlin_speeds[0]); ok_stop = false; } if( merlin_speeds[1] > 0 ) { merlin_send_angle(1, merlin_speeds[1]); ok_stop = false; } } } else if( motors_clear == true && ! motor_sl_mod && ( merlin_enabled && ( merlin_flags & B11000000 ) ) ) { // merlin motors are currently running! // other actions cannot continue... if( ! merlin.readAxisStatus(1) && ! merlin.readAxisStatus(2) ) { // but not actually running... now remove the flags // indicating so. merlin_flags &= B00111111; ok_stop = true; } } else if( motors_clear == true && ! motor_sl_mod && ( m_sms_tm[0] > 0 || m_sms_tm[1] > 0 ) ) { // if we're set to go to s-m-s and at least one motor is set to move // start DC motor(s) moving motor_ran = 0; // set motors to move, and then // set timer to turn them off if( m_sms_tm[0] > 0 ) { // start first motor run_motor_sms(0); MsTimer2::set(m_sms_tm[0], stop_motor_sms); } else if( m_sms_tm[1] > 0 ) { // start second motor (see state // handling above, where motor_engaged is set) run_motor_sms(1); MsTimer2::set(m_sms_tm[1], stop_motor_sms); } // engage timer MsTimer2::start(); motor_engaged = true; motors_clear = false; ok_stop = false; } else if( external_interval & B11000000 ) { // external intervalometer is engaged if( external_interval & B00100000 ) { // external intervalometer has triggered // clear out ok to fire flag external_interval &= B11011111; do_fire = true; } } else if( cam_last_tm < millis() - (cam_interval * 1000) ) { // internal intervalometer triggers do_fire = true; } if( do_fire == true ) { // we've had a fire camera event // is the external trigger to fire? (either as 'before' or 'through') if( external_trigger & B11000000 && ext_trig_pre_delay > 0 && ext_trip == false && (cam_repeat == 0 || cam_repeated == 0) ) { alt_ext_trigger_engage(true); ext_trip = true; } else { // make sure we handle pre-focus tap timing if( ( pre_focus_clear == 4 || focus_tap_tm == 0 || (cam_repeat > 0 && cam_repeated > 0) ) && !(run_status & B00001000) ) { // we always set the start mark at the time of the // first exposure in a repeat cycle (or the time of exp // if no repeat cycle is in play if( cam_repeat == 0 || cam_repeated == 0 ) cam_last_tm = millis(); // deal with camera repeat actions if( cam_repeat == 0 || (cam_repeat > 0 && cam_repeated >= cam_repeat) ) { camera_fired = true; do_fire = false; ext_trip = false; cam_repeated = 0; } else if( cam_repeat > 0 ) { // only delay ater the first shot if( cam_repeated > 0 ) delay(cam_rpt_dly); // blocking delay between camera firings (we should fix this later!) cam_repeated++; } // camera is all clear to fire, and enough // time is elapsed fire_camera(exp_tm); pre_focus_clear = 0; } else if( focus_tap_tm > 0 && pre_focus_clear == 0 && !(run_status & B00001000) ) { // pre-focus tap is set, bring focus line high digitalWrite(FOCUS_PIN, HIGH); MsTimer2::set(focus_tap_tm, stop_cam_focus); MsTimer2::start(); pre_focus_clear = 1; } } // end else (not external trigger... } // end if(do_fire... } void start_executing() { // starts program execution run_status |= B10010000; // turn on motors motor_control(0,true); motor_control(1,true); // if ramping is enabled for a motor, start at a zero // speed if( m_ramp_set[0] >= 1 ) motor_set_speed(0, 0); if( m_ramp_set[1] >= 1 ) motor_set_speed(1, 0); // reset shot counter shots = 0; } void stop_executing() { run_status &= B01100111; motor_stop_all(); }Ещё
void init_user_interface() { pinMode(LCD_BKL, OUTPUT); // turn on lcd backlight analogWrite(LCD_BKL, 255); // init lcd to 16x2 display lcd.begin(16, 2); lcd.setCursor(0,0); // clear and turn on autoscroll lcd.clear(); //lcd.autoscroll(); // banner lcd.setCursor(0,0); //vstavil ja lcd.print(" 2012"); lcd.setCursor(0,1); // 5,1 lcd.print(" VVR"); delay(1500); lcd.clear(); lcd.setCursor(0,0); lcd.print(" Dolly - Pan"); lcd.setCursor(0,1); // 4,1 lcd.print("Timelapse Engine"); // setup button input pinMode(BUT_PIN, INPUT); // enable internal pull-up digitalWrite(BUT_PIN, HIGH); // set the update screen flag (draw main // screen) ui_ctrl_flags |= B10000000; delay(3000); } void check_user_interface() { // turn off/on lcd backlight if needed if( ui_ctrl_flags & B00000010 && input_last_tm < millis() - (lcd_dim_tm * 1000) ) { ui_ctrl_flags &= B11111101; if( blank_lcd ) lcd.noDisplay(); digitalWrite(LCD_BKL, LOW); } else if( ! (ui_ctrl_flags & B00000010) && input_last_tm > millis() - (lcd_dim_tm * 1000) ) { ui_ctrl_flags |= B00000010; lcd.display(); analogWrite(LCD_BKL, cur_bkl); } // if we're set to update the display // (on-demand or once a second when not // in a menu) if( ui_ctrl_flags & B10000000 || ( (ui_update_tm < millis() - 1000) && ! (ui_ctrl_flags & B01000000) ) ) { // determine whether to show home or manual screen if( ! ( ui_ctrl_flags & B00000100 ) ) { show_home(); } //#ifdef MERLIN_ENABLED else if( merlin_flags & B00100000 ) { show_merlin(); } //#endif else { show_manual(); } ui_ctrl_flags &= B01111111; ui_update_tm = millis(); } byte held = ui_button_check(); // make sure to turn off motor if in manual // control and no button is held if( ui_ctrl_flags & B00000100 && held == false && run_status & B00010000 ) motor_control(cur_motor, false); } byte ui_button_check() { static byte hold_but_cnt = 0; get_button_pressed(); boolean held = false; for( byte i = BUT0; i <= BUT4; i++) { byte bt_press = is_button_press( i ); if( bt_press == 0 ) continue; if( bt_press == 1 && millis() - input_last_tm > HOLD_BUT_MS ) { // button is pressed hold_but_cnt = 0; inp_val_mult = 1; input_last_tm = millis(); handle_input(i, false); } else if( bt_press == 2 ) { held = true; // button being held if( hold_but_tm <= millis() - HOLD_BUT_MS) { hold_but_tm = millis(); input_last_tm = millis(); handle_input(i, true); hold_but_cnt++; if( hold_but_cnt >= 8 ) { inp_val_mult = inp_val_mult >= 1000 ? 1000 : inp_val_mult * HOLD_BUT_VALINC; hold_but_cnt = 0; } } } // end else if button press state == 2 } // end for loop return(held); } void get_button_pressed() { // see which buttons are pressed // buttons are on one analog pin, the value // determines which button, if any is pressed // read analog input int val_read = analogRead(BUT_PIN - 14); // don't let it flip in a single read if( abs(last_but_rd - val_read) > BUT_THRESH ) { last_but_rd = val_read; button_pressed = 0; return; } if( val_read > (BUT0_VAL - BUT_THRESH) && val_read < (BUT0_VAL + BUT_THRESH) ) { button_pressed = BUT0; } else if( val_read > (BUT1_VAL - BUT_THRESH) && val_read < (BUT1_VAL + BUT_THRESH) ) { button_pressed = BUT1; } else if( val_read > (BUT2_VAL - BUT_THRESH) && val_read < (BUT2_VAL + BUT_THRESH) ) { button_pressed = BUT2; } else if( val_read > (BUT3_VAL - BUT_THRESH) && val_read < (BUT3_VAL + BUT_THRESH) ) { button_pressed = BUT3; } else if( val_read > (BUT4_VAL - BUT_THRESH) && val_read < (BUT4_VAL + BUT_THRESH) ) { button_pressed = BUT4; } else { button_pressed = 0; } } byte is_button_press(byte button) { // determine if the given button was // pressed, held, or is neither static byte button_was = 0; // if the button is set as 'active' if( button_pressed == button ) { // if we have already registered a press without // registering a non-press if( button_was ) { // increase 'skip hold count' return(2); } // button was not previous pressed... button_was = button; hold_but_tm = millis(); return(1); } // if button set as inactive // if button was previously set as active, // register previous state as inactive if( button_was == button ) { button_was = 0; // set button as not currently pressed button_pressed = 0; } return(0); } byte get_menu( byte mnu, byte pos ) { // where is our target menu when // mnu.pos is pressed? switch(mnu) { case 0: switch(pos) { case 0: return(1); case 1: return(2); case 2: return(3); case 3: return(4); case 4: return(5); case 5: return(6); } break; case 1: // manual control is special return code return(254); default: break; } // default is 'no target', an input value return(255); } /* handle user input */ void handle_input( byte button, boolean held ) { // do what needs to be done when whatever // button is hit if( button == BUT_CT ) { // call center button function ui_button_center(held); } else if( button == BUT_DN ) { ui_button_down(held); } else if( button == BUT_UP ) { ui_button_up(held); } else if( button == BUT_RT ) { ui_button_rt(held); } else if( button == BUT_LT ) { ui_button_lt(held); } return; } // button handlers void ui_button_center( boolean held ) { // center button // on calibration screen if( ui_ctrl_flags & B00000001 ) { if( ui_cal_scrn_flags & B01000000 ) { // completed calibrating ui_cal_scrn_flags &= B00111111; show_calibrate(); return; } else if( ui_cal_scrn_flags & B10000000 ) { // in calibrating input if( held == true ) return; m_cal_done = true; return; } execute_calibrate(); return; } if( main_scr_input > 0 ) { // make sure to abort main screen input lcd.noBlink(); main_scr_input = 0; } // if in manual control if( ui_ctrl_flags & B00000100 ) { // clear out manual ctrl flag ui_ctrl_flags &= B11111011; merlin_flags &= B11011111; // resume back to setup menu ui_ctrl_flags |= B01000000; cur_menu = 1; // show manual menu again draw_menu(0, false); return; } if( ! (ui_ctrl_flags & B01000000) ) { // not in any setup menu ui_ctrl_flags |= B01000000; cur_menu = 0; draw_menu(0,false); } else { // in a setup menu, find // the next menu to go to // calibration, don't do anything else if( (cur_menu == 2 || cur_menu == 3) && cur_pos == 6 ) { get_value(cur_menu, cur_pos, false); return; } byte new_menu = get_menu(cur_menu, cur_pos); // if drawing motor manual screen... if( new_menu == 254 ) { get_value(cur_menu, cur_pos, false); return; } if( new_menu == 255 && ! (ui_ctrl_flags & B00100000) ) { // this is not a menu, but an input of some // sort draw_menu(3,true); return; } else if( ui_ctrl_flags & B00100000 ) { // exiting out of value entry (save...) // go to previous menu // clear the cursor position cur_inp_pos = 0; // read value back from input get_value(cur_menu, cur_pos, true); // reset the float tenths (vs 100ths) parameter ui_float_tenths = false; // clear in value setting flag // and the flag indicating that // we've already displayed this value ui_ctrl_flags &= B11001111; draw_menu(0,false); } else { // entering another menu // record the last menu we were at push_menu(cur_menu); // clear in value setting flag ui_ctrl_flags &= B11011111; // set menu to new menu cur_menu = new_menu; draw_menu(0,false); } } } void ui_button_down( boolean held ) { // on calibration screen if( ui_ctrl_flags & B00000001 ) { if( ui_cal_scrn_flags & B10000000 ) { // in calibrating settings move_val(false); update_cal_screen(); return; } m_cur_cal = m_cur_cal > 0 ? m_cur_cal - 1 : 0; show_calibrate(); return; } // if in manual motor mode... if( ui_ctrl_flags & B00000100 ) { if( merlin_flags & B00100000 ) { if( held == true ) return; if( merlin_flags & B01000000 ) { merlin_stop(1); // speed was modified by running, return to // original value merlin_set_speed(1, merlin_speeds[1]); } else { merlin.setSpeed(2, merlin_man_spd[1]); merlin_set_dir(1,1); merlin_run(1); } show_merlin(); } else { motor_speed_adjust(cur_motor, -1 * inp_val_mult, true); show_manual(); } return; } // if not currently in setup menus, or // modifying a main screen value if( ! (ui_ctrl_flags & B01000000) & main_scr_input == 0 ) return; if( main_scr_input > 0 ) { move_val(false); // save present value get_mainscr_set(main_scr_input, true); // set screen to update ui_ctrl_flags |= B10000000; } else if( ui_ctrl_flags & B00100000 ) { // entering a value move_val(false); draw_menu(3,true); } else { // moving to next menu item draw_menu(2,false); } } void ui_button_up( boolean held ) { // on calibration screen if( ui_ctrl_flags & B00000001 ) { if( ui_cal_scrn_flags & B10000000 ) { // in calibrating settings move_val(true); update_cal_screen(); return; } m_cur_cal = m_cur_cal > 1 ? 2 : m_cur_cal + 1; show_calibrate(); return; } // if in manual motor mode... if( ui_ctrl_flags & B00000100 ) { if( merlin_flags & B00100000 ) { if( held == true ) return; if( merlin_flags & B01000000 ) { merlin_stop(1); // speed was modified by running, return to // original value merlin_set_speed(1, merlin_speeds[1]); } else { merlin.setSpeed(2, merlin_man_spd[1]); merlin_set_dir(1,0); merlin_run(1); } show_merlin(); } else { motor_speed_adjust(cur_motor,1 + inp_val_mult, true); show_manual(); } return; } if( ! (ui_ctrl_flags & B01000000) & main_scr_input == 0 ) { // alternate between merlin and home screens if( merlin_enabled ) { // switch between merlin and normal home screens if( merlin_flags & B00010000 ) { merlin_flags &= B11101111; } else { merlin_flags |= B00010000; } return; } } if( main_scr_input > 0 ) { move_val(true); // save present value get_mainscr_set(main_scr_input, true); // set screen to update ui_ctrl_flags |= B10000000; } else if( ui_ctrl_flags & B00100000 ) { // entering a value move_val(true); draw_menu(3,true); } else { draw_menu(1,false); } } void ui_button_rt( boolean held ) { // clear out calibration screen value, if // set if( ui_ctrl_flags & B00000001 ) ui_ctrl_flags &= B11111110; // if in manual control if( ui_ctrl_flags & B00000100 ) { if( merlin_flags & B00100000 ) { if( held == true ) return; if( merlin_flags & B10000000 ) { merlin_stop(0); // speed was modified by running, return to // original value merlin_set_speed(0, merlin_speeds[0]); } else { merlin.setSpeed(1, merlin_man_spd[0]); merlin_set_dir(0,0); merlin_run(0); } //show_merlin(); } else { // in manual if( held == true ) { // change motor direction motor_dir(cur_motor, 0); if( ! (run_status & B00010000) ) motor_control(cur_motor, true); } show_manual(); } return; } if( ! (ui_ctrl_flags & B01000000) ) { // we're on main screen, rt switches value we can // adjust main_screen_select(true); return; } if( ui_ctrl_flags & B00100000 ) { // we're in a value entry mode. Exit // entry without saving the value // clear in value setting flag // and the flag indicating that // we've already displayed this value ui_ctrl_flags &= B11001111; // reset the float tenths (vs 100ths) parameter ui_float_tenths = false; draw_menu(0,false); return; } // draw previous menu if( cur_menu == 0 ) { // we're at the highest menu, back to main screen cur_pos = 0; // clear setup flag // indicate display needs updating ui_ctrl_flags &= B10111111; ui_ctrl_flags |= B10000000; // clear out list of menus flush_menu(); } else { // a parent menu can be drawn cur_menu = pop_menu(); draw_menu(0,false); } } void ui_button_lt(boolean held) { // if in manual control if( ui_ctrl_flags & B00000100 ) { if( merlin_flags & B00100000 ) { if( held == true ) return; if( merlin_flags & B10000000 ) { merlin_stop(0); // speed was modified by running, return to // original value merlin_set_speed(0, merlin_speeds[0]); } else { merlin.setSpeed(1, merlin_man_spd[0]); merlin_set_dir(0,1); merlin_run(0); } } else { if( held == true ) { // change motor direction motor_dir(cur_motor, 1); // get motor moving (if not already) if( ! (run_status & B00010000) ) motor_control(cur_motor, true); } show_manual(); } return; } if( ! (ui_ctrl_flags & B01000000) ) { // we're on main screen, lt switches value we can // adjust main_screen_select(false); return; } // left button if( ! (ui_ctrl_flags & B01000000) || ui_ctrl_flags & B00100000 ) return; } /* Draw screens */ void draw_menu(byte dir, boolean value_only) { // turn off blinking, if on... lcd.noBlink(); boolean draw_all = false; // determine the direction we are going, up/down (1/2), // draw all (but don't move position) (3), and draw // new menu from top (0) if( dir == 2 ) { // down cur_pos++; if( cur_pos > cur_pos_sel ) { lcd.clear(); draw_all = true; } } else if( dir == 1 ) { // up cur_pos = cur_pos == 0 ? 0 : cur_pos - 1; if( cur_pos < cur_pos_sel ) { lcd.clear(); draw_all = true; } } else if( dir == 3 ) { // draw all (no move) draw_all = true; } else { // draw new menu (from top) cur_pos = 0; draw_all = true; } // don't overrun the memory locations for this menu cur_pos = cur_pos > max_menu[cur_menu] ? max_menu[cur_menu] : cur_pos; switch( cur_menu ) { case 0: draw_values(menu_str, draw_all, value_only); break; case 1: draw_values(man_str, draw_all, value_only); break; case 2: draw_values(axis0_str, draw_all, value_only); break; case 3: draw_values(axis1_str, draw_all, value_only); break; case 4: draw_values(cam_str, draw_all, value_only); break; case 5: draw_values(set_str, draw_all, value_only); break; case 6: draw_values(scope_str, draw_all, value_only); break; default: return; } } void draw_values(const char *these[], boolean draw_all, boolean value_only) { if( draw_all == true ) { // must draw the whole display lcd.clear(); // clear out lcd buffer memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); // draw first option lcd.noCursor(); lcd.setCursor(0,0); cur_pos_sel = cur_pos; strcpy_P(lcd_buf, (char*) pgm_read_word(&(these[cur_pos]))); lcd.print("> "); lcd.print(lcd_buf); lcd.setCursor(0,1); // if we're not displaying only a value, and there's // another menu entry to display -- display it on the // second line.. if( ! value_only ) { if( cur_pos + 1 <= max_menu[cur_menu] ) { cur_pos_sel = cur_pos + 1; memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); strcpy_P(lcd_buf, (char*)pgm_read_word(&(these[cur_pos + 1]))); lcd.print(" "); lcd.print(lcd_buf); } // clear out in value entry setting, if set ui_ctrl_flags &= B11011111; } else { // display the value of the current entry ui_ctrl_flags |= B00100000; if(! ( ui_ctrl_flags & B00010000 ) ) { // have just drawn this value // place value from variable into // temporary buffer get_value(cur_menu, cur_pos, false); ui_ctrl_flags |= B00010000; } // display the correct current // temporary input value if( ui_type_flags2 & B10000000 ) { // for alt i/o inputs if( cur_inp_long == 0 ) { lcd.print("Disabled"); } else if( cur_inp_long == 1 ) { lcd.print("Start"); } else if( cur_inp_long == 2 ) { lcd.print("Stop"); } else if( cur_inp_long == 3 ) { lcd.print("Toggle"); } else if( cur_inp_long == 4 ) { lcd.print("Ext. Interval."); } else if( cur_inp_long == 5 ) { lcd.print("Out Before"); } else if( cur_inp_long == 6 ){ lcd.print("Out After"); } else { lcd.print("Out Both"); } return; } else if( ui_type_flags2 & B01000000 ) { // cal speed inputs in gobal set menu display_spd_ipm(cur_inp_long, 0); return; } switch(ui_type_flags) { case B10000000: lcd.print(cur_inp_float, (byte) 2); break; case B01000000: if (cur_inp_bool == true) { lcd.print("On"); } else { lcd.print("Off"); } break; case B00100000: if (cur_inp_bool == true) { lcd.print("Up"); } else { lcd.print("Dn"); } break; case B00010000: if (cur_inp_bool == true) { lcd.print("Rt"); } else { lcd.print("Lt"); } break; case B00001000: if (cur_inp_bool == true) { lcd.print("IPM"); } else { lcd.print("PCT"); } break; case B00000100: if (cur_inp_bool == true) { lcd.print("Pulse"); } else { lcd.print("Interleave"); } break; case B00000010: if (cur_inp_bool == true) { lcd.print("Rotary"); } else { lcd.print("Linear"); } break; case B00000001: if( cur_inp_long == 0 ) { lcd.print(0,DEC); } else if( cur_inp_long == 1 ) { lcd.print(45,DEC); } else { lcd.print(90,DEC); } break; default: lcd.print((unsigned long)cur_inp_long); return; } } } // end if( draw_all == true else { // do not need to re-draw the whole screen // move cursor down if we're not in // a value input screen if( ! (ui_ctrl_flags & B00100000) ) { lcd.setCursor(0,0); lcd.print(' '); lcd.setCursor(0,1); lcd.print('>'); } } } /* Menu history functions */ void push_menu(byte this_menu) { // push the given entry to the end of the list for( byte i = 0; i < sizeof(hist_menu) / sizeof(hist_menu[0]); i++) { if( hist_menu[i] == 0 ) { hist_menu[i] = this_menu; } } } byte pop_menu() { byte bk_menu = 0; for( byte i = sizeof(hist_menu) / sizeof(hist_menu[0]); i > 0 ; i--) { if( hist_menu[i-1] != 0 ) { bk_menu = hist_menu[i-1]; hist_menu[i-1] = 0; } } return(bk_menu); } void flush_menu() { memset(hist_menu, 0, sizeof(hist_menu) / sizeof(hist_menu[0])); }Ещё
void prep_home_screen() { lcd.clear(); lcd.setCursor(0,0); if( run_status & B10000000 ) { // in 'external intervalometer' mode, show 'ext' inseatd of 'on' if( external_interval & B11000000 ) { lcd.print("Ext"); } else { lcd.print("On"); } } else { lcd.print("Off"); } lcd.setCursor(4, 0); } void show_merlin_home() { lcd.print(" Scope"); lcd.setCursor(0,1); if( merlin_dir[0] == 1 ) { lcd.print('L'); } else { lcd.print('R'); } display_spd_merlin(merlin_speeds[0], 0); lcd.setCursor(8,1); if( merlin_dir[1] == 1 ) { lcd.print('D'); } else { lcd.print('U'); } display_spd_merlin(merlin_speeds[1], 1); // we call this here mainly to reset the // cursor position when in an input if( main_scr_input ) get_merlin_set(main_scr_input, false); } void show_home() { prep_home_screen(); if( merlin_flags & B00010000 ) { // show merlin screen instead show_merlin_home(); return; } // deal with interval times that are less than total time // required between shots float i_total = calc_total_cam_tm(); if( cam_interval < i_total ) { lcd.print(i_total, 1); } else { lcd.print((float) cam_interval, 1); } lcd.print("s "); if( shots > 999 ) { lcd.setCursor(10,0); } else if( shots > 99 ) { lcd.setCursor(11, 0); } else if( shots > 9 ) { lcd.setCursor(12, 0); } else { lcd.setCursor(13,0); } lcd.print('['); lcd.print(shots, DEC); lcd.print(']'); lcd.setCursor(0,1); if( m_wasdir[0] == 1 ) { lcd.print('L'); } else { lcd.print('R'); } if( m_type[0] == 1 ) { display_spd_deg(m_speeds[0], 0); } else if( ui_motor_display ) { // display pct display_spd_ipm(m_speeds[0], 0); } else { display_spd_pct(m_speeds[0]); } lcd.setCursor(8,1); if( m_wasdir[1] == 1 ) { lcd.print('L'); } else { lcd.print('R'); } if( m_type[1] == 1 ) { display_spd_deg(m_speeds[0], 1); } else if( ui_motor_display ) { // display pct display_spd_ipm(m_speeds[1], 1); } else { display_spd_pct(m_speeds[1]); } // we call this here mainly to reset the // cursor position when in an input if( main_scr_input ) get_mainscr_set(main_scr_input, false); } void main_screen_select(boolean dir) { if( main_scr_input == 0 && dir == true ) { lcd.blink(); } if( dir ) { main_scr_input++; } else { main_scr_input--; } // merlin screen has five inputs, normal home // has six byte max_inputs = (merlin_flags & B00010000) ? 5 : 6; // exit main scr setup if( (dir == true && main_scr_input > max_inputs) || (dir == false && main_scr_input == 0 ) ) { lcd.noBlink(); main_scr_input = 0; return; } get_mainscr_set(main_scr_input, false); } void show_manual() { ui_ctrl_flags |= B00000100; lcd.clear(); lcd.noBlink(); lcd.setCursor(0, 0); if( cur_motor == 0 ) { lcd.print("Motor Rot-Pan"); } else { lcd.print("Motor Lin"); } lcd.setCursor(0, 1); lcd.print("Speed: "); if( ui_motor_display ) { // display ipm display_spd_ipm(m_speeds[cur_motor], cur_motor); } else { display_spd_pct(m_speeds[cur_motor]); } } void show_calibrate() { // show the motor calibrate screen ui_ctrl_flags |= B00000001; lcd.clear(); lcd.noBlink(); lcd.setCursor(0,0); lcd.print("Cal M"); lcd.print(cur_motor + 1, DEC); lcd.print(" ["); byte angle = m_cur_cal * 45; lcd.print(angle, DEC); lcd.print(" Deg]"); } void execute_calibrate() { // in calibration ui_cal_scrn_flags |= B10000000; // floating point input ui_type_flags |= B10000000; ui_float_tenths = false; byte was_cur_pos = 0; byte completed = 0; // sms calibration for( byte i = 0; i <= 1; i++ ) { float traveled = 0.01 * (max_ipm[cur_motor]); unsigned int runspd = 0.01 * m_maxsms[cur_motor]; cur_inp_float = traveled; completed++; lcd.clear(); lcd.setCursor(0,0); lcd.print("Running "); lcd.print('['); lcd.print(completed, DEC); lcd.print(" of 6]"); // sms moving in i dir // at 6% of total distance motor_run_calibrate(1, runspd, i); update_cal_screen(); m_cal_done = false; while( m_cal_done == false ) { byte held = ui_button_check(); } m_cal_array[cur_motor][m_cur_cal][0][i] = traveled / cur_inp_float; } // save this for now, to avoid calc problems byte was_smsfx[2] = { m_smsfx[0], m_smsfx[1] }; m_smsfx[0] = 0; m_smsfx[1] = 0; // pulse calibration for( byte c = 1; c <= 2; c++ ) { byte ths_spd = c == 1 ? motor_spd_cal[0] : motor_spd_cal[1]; for( byte i = 0; i <= 1; i++ ) { float des_ipm = motor_calc_ipm(cur_motor, ths_spd, true); cur_inp_float = des_ipm; completed++; lcd.clear(); lcd.setCursor(0,0); lcd.print("Running "); lcd.print('['); lcd.print(completed, DEC); lcd.print(" of 6]"); // pulse moving in i dir motor_run_calibrate(2, ths_spd, i); update_cal_screen(); m_cal_done = false; while( m_cal_done == false ) { byte held = ui_button_check(); } m_cal_array[cur_motor][m_cur_cal][c][i] = ( cur_inp_float / des_ipm ); } } // restore values m_smsfx[0] = was_smsfx[0]; m_smsfx[1] = was_smsfx[1]; ui_cal_scrn_flags &= B01111111; ui_cal_scrn_flags |= B01000000; // save values to memory // handle m_cal_array in a sane manner // float m_cal_array[2][3][3][2] // 2 * 3 * 3 * 2 * 4 = 144 byte* p = (byte*)(void*)&m_cal_array; eeprom_write(71, *p, 144); } void update_cal_screen() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Dist Moved:"); lcd.setCursor(0,1); lcd.print(cur_inp_float, 2); }На дисплей lcd 16x2 выводится меню из шести строк для управления мотором. Всё работает. Нужно подключить lcd 20x4. Инициировал. Написал заставку - выводится на все четыре строчки . Меню выводится только на две строки. В чём проблема ?
Проблема в том скорее всего что он состоит из двух 20х2 и есть два пина enable смотрите даташит.
Инициализацию проводите примерно так
LiquidCrystal lcd1(8, 6, 5, 4, 3, 2);
LiquidCrystal lcd2(8, 11, 5, 4, 3, 2);
lcd1.begin(20, 2);
lcd2.begin(20, 2);
Будет две строки верхние и две нижние, такой способ применил для 40х4
Дисплей Winstar WN2004d с одним E. Работают все строчки. Проблема в программе наверное. Но как сделать, что бы на дисплей выводилось 4 стоки меню у меня знаний не хватает.
У вас в коде инициализация только двух строк lcd.begin(16, 2);
Замените на lcd.begin(16, 4);
Да, да или lcd.begin(20, 4); попробуйте.
Я всё пробовал с lcd.begin(20,4). замена на 16,4 ничего не даёт. Нашёл часть кода связанного с отображением меню. Ночью пробовал разные комбинации дописывать -курсор всё равно только на двух строчках, а в меню появляются глючные записи на 4 строке
// draw first option lcd.noCursor(); lcd.setCursor(0,0); cur_pos_sel = cur_pos; strcpy_P(lcd_buf, (char*) pgm_read_word(&(these[cur_pos]))); lcd.print("> "); lcd.print(lcd_buf); lcd.setCursor(0,1); // if we're not displaying only a value, and there's // another menu entry to display -- display it on the // second line.. if( ! value_only ) { if( cur_pos + 1 <= max_menu[cur_menu] ) { cur_pos_sel = cur_pos + 1; memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); strcpy_P(lcd_buf, (char*)pgm_read_word(&(these[cur_pos + 1]))); lcd.print(" "); lcd.print(lcd_buf); } // clear out in value entry setting, if set ui_ctrl_flags &= B11011111;После проб и ошибок заставил меню появиться на всех четырёх строчках. осталось заставить курсор бегать по 3 и 4 строке
void draw_values(const char *these[], boolean draw_all, boolean value_only) { if( draw_all == true ) { // must draw the whole display lcd.clear(); // clear out lcd buffer memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); // draw first option lcd.noCursor(); lcd.setCursor(0,0); cur_pos_sel = cur_pos; strcpy_P(lcd_buf, (char*) pgm_read_word(&(these[cur_pos]))); lcd.print("> "); lcd.print(lcd_buf); lcd.setCursor(0,1); // if we're not displaying only a value, and there's // another menu entry to display -- display it on the // second line.. if( ! value_only ) { if( cur_pos + 1 <= max_menu[cur_menu] ) { cur_pos_sel = cur_pos + 1; memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); strcpy_P(lcd_buf, (char*)pgm_read_word(&(these[cur_pos + 1]))); lcd.print(" "); lcd.print(lcd_buf); lcd.setCursor(0,2); } if( cur_pos + 2 <= max_menu[cur_menu] ) { cur_pos_sel = cur_pos + 2; memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); strcpy_P(lcd_buf, (char*)pgm_read_word(&(these[cur_pos + 2]))); lcd.print(" "); lcd.print(lcd_buf); lcd.setCursor(0,3); } if( cur_pos + 3 <= max_menu[cur_menu] ) { cur_pos_sel = cur_pos + 3; memset(lcd_buf, ' ', sizeof(char) * MAX_LCD_STR); strcpy_P(lcd_buf, (char*)pgm_read_word(&(these[cur_pos + 3]))); lcd.print(" "); lcd.print(lcd_buf); } ui_ctrl_flags &= B11011111;Полное решение не скажу, но думается мне, что вам должно помочь следующее:
в методе "draw_values" при условии draw_all == false надо написать как то так:
else { // do not need to re-draw the whole screen // move cursor down if we're not in // a value input screen if( ! (ui_ctrl_flags & B00100000) ) { lcd.setCursor(0, cur_pos - 1); lcd.print(' '); lcd.setCursor(0, cur_pos); lcd.print('>'); } }Это должно помочь с движением курсора ниже второй строчки.
А вообще, было бы неплохо вам чутка порефакторить код :)
Сейчас попробую ваш вариант. Серьёзно изменить код знаний не хватает. Сейчас всё работает - боюсь влезу глубоко и потом управление накроется.
Сейчас попробую ваш вариант. Серьёзно изменить код знаний не хватает. Сейчас всё работает - боюсь влезу глубоко и потом управление накроется.
Можно начать с простого, например разбить большие методы на более маленькие, дав им при этом осмысленные имена. Так же можно сгруппировать и разнести методы по разным исходным файлам, например: методы для работы с меню в Menu.cpp(.h), методы для работы с кнопками в Keyboard.cpp(.h) и т.д. Это сильно поможет в поддержке кода.
Курсор пошёл на 3 и 4 строчки, но не удаляется с предыдущих. Попробовал поиграться в строчках
if( dir == 2 ) { // down cur_pos++; if( cur_pos > cur_pos_sel ) { lcd.clear draw_all = true; }Вот что получается. При замене cur_pos на (cur_pos + 2) курсор работает только на двух строках, а потом поднимается меню.
При (cur_pos + 3 ) курсор вообще на месте, а поднимается меню.
В какой ситуации не удаляется? При движении вверх или вниз?
Меню из шести строк. Сначала на дисплее 4 строки. Курсор при нажатии кнопки опускается вниз и остаётся на каждой строке. При переходе на 5 строку меню на дисплее выведено две строки (5 и 6). Курсор на первой и последней (4) строке дисплея. При нажатии кнопки курсор бегает по дисплею произвольно и не в начале строк. Всё это при движени вниз. Движение курсора вверх изначально было нормально.
Эм... странно, вот тот кусочек кода, что я скинул как раз и должен удалять старый курсор при движении вниз. Перепроверьте, что пробел выводится именно в положении (0, cur_pos - 1).
lcd.setCursor(0, cur_pos - 1); lcd.print(' ');Нашёл у себя ошибку. Теперь курсор бегает вниз нормально, но при переходе на 5 и 6 строки меню глюки как писал выше.
Для дисплея 16х2 написано
А как для дисплея 20х4 ?
логично предположить, что 21: 20 под отображаемые символы и 1 под символ перевода строки '\n' (или скорее - под символ конца строки - '\0')
Спасибо
Вечер добрый.
Подскажите. Есть LCD 20*4 совершенно левая, но рабочая, проверял на параллельном порту ПК.
Можно подключать панельки сторонние, а не те что на сайте производителя?
ВОт еще нашел LiquidCrystalRus.h
#include <LineDriver.h> #include <LiquidCrystalExt.h> #include <LiquidCrystalRus.h> LiquidCrystalRus lcd(12, 11, 5, 4, 3, 2); void setup() { lcd.begin(16, 2); lcd.print("Здравствуй, мир!"); } void loop() { lcd.setCursor(0, 1); lcd.print(millis() / 1000); }НУ если кому интересно.
Качать тут https://github.com/RoboCraft/LiquidCrystalRus/zipball/master
Можно подключать панельки сторонние, а не те что на сайте производителя?
какие панели имеются ввиду?
Сторонних производителей. К примеру 20*4 размером 150мм х 60мм
Незнаю как описать, у меня 20*4 с 3-мя чипами с зади.
Занимался модингом лицевых панелей ПК, ранее. Вот осталось.
Сторонних производителей. К примеру 20*4 размером 150мм х 60мм
Незнаю как описать, у меня 20*4 с 3-мя чипами с зади.
Занимался модингом лицевых панелей ПК, ранее. Вот осталось.
если у вас голый индикатор, то никакой панели для подключения не нужно - проводами подключайте.
Спасибо.
Доброго времени суток господа!
Имел не осторожность раздербанить устройство и извлечь из него дисплей 20х4 BT42008 LED.
Решил его подключить к UNO по стандартной схеме для начинающих и... ничего не получилось. Не кажет!!!
Подключил обратно в старое устройство все работает.
Что может быть не так? Подскажите посоветуйте!!!
Ну, давай, по порядку:
1. Подали питание - (в двух местах) - есть "контрольная" строка? загораются "квадратики"?
2. Параллельную шину как используешь - 4 или 8 бит? (при подключении должна пропасть контрастность на "контрольной строке")
--------------------------------------
Подключал разные панельки и все без проблем. ПОНЯЛ только одно! какие библиотеки юзаем.
Подключался по вот этому примеру.
http://wiki.amperka.ru/схемы-подключения:подключение-текстового-экрана
нашел даташит на экран все один в один как в примере.
по первому пункту контрольной строки не было
по второму 4 бит
А в листинге, в инициализации, 20х4 ставишь?
Воевал с "бубном" при подключении графических дисплеев, но вроде побеждал всегда.
А тут то, два провода
КРУТИ КОНТРАСТНОСТЬ. посмотрел таташид - стандартный контроллер
http://robocraft.ru/blog/arduino/503.html или
http://audio.probudget.ru/arduino/arduino-i-lcd-ekran
Вот так подключи. Контакты 1,2 и 15,16 - питание!!!!!!!!!!!!!!!
И учти, во многих статьях есть специальные опечатки! Что бы сами думали.
да меняю с 16x2 на 20х4
пробывал ни хухе(
(блин надо руки пересаживать:))
Бесполезно не могу запустить(
что еще можно подумать?
Для начала , подключи 1,2 и 15,16
Должна загореться строка "кубиками"
Покажи ФОТО с обеих сторон
Точно такая же ситуация, не могу победить дисплей BONO MC2402. Подключен по даташиту (пробовал и 4 и 8 бит). Ни с одной библиотекой не работает, при этом кубики в верхней строке есть и контрастность регулируется. То ли команды инициализации другие, то ли что-то не то с распиновкой выводов :(
фото дисплея?
потыкал мультиметром и определил, что первый выход 0V, второй +5V, а с третьим загвоздка при замере между 1 и 3 напряжение около -6,5V между 2 и 3 - 12V вот как то так)))
И как это сделать мемогу догнать помогите!!! HELP ME!!!
ВОТ тут скачать можно
Ну, начну с самого начала.
Как и писал. подкидываем питание и смотрим, должна показать высокий контраст одна из строк (обычно первая). В принципе убить чипы, путем не правильного подключения нельзя, но статикой убиваются мгновенно, сам пару спалил, пока понял, что капут дисплеям.
После питания, подключаем данные. Заливаем тест сетча.
НО. я уде давно использую шину I2C . Проводов меньше и дискретчики лишними не бывают.
Вот тут покупаю, если быстро надо и дешево, или по ссылке выше, но долго.
результат.
А вот и полный пример, с промежуточными результатами.
Мозг кипит, а каши нету!
По третьему пункту не могу понять хоть убей(
Может кто разъяснит?
А какой результат по такому подключению?
И давай на почту, а тут результат работы выставишь!
А то модераторы забанят, за флуд на сайте.
korzhavin@gmail.com
Имел не осторожность раздербанить устройство и извлечь из него дисплей 20х4 BT42008 LED.
Что за девайс разобрал??
Здравствуйте друзья! Заказал на али дисплей 20х4 Noname. Попытки подключить не увенчались успехом. Подскажите пожалуйста, при подаче питания должно что либо высветиться на дисплее? Он у меня молчит...
Здравствуйте друзья! Заказал на али дисплей 20х4 Noname. Попытки подключить не увенчались успехом. Подскажите пожалуйста, при подаче питания должно что либо высветиться на дисплее? Он у меня молчит...
Там подстроечным резистором 10k (он бывает сразу на дисплее) нужно контраст выставлять.
Спасибо дружище огромнейшее! Схема очень помогла. А в заблуждение меня ввел старый LCD, который при подаче на 1 и 2 ногу питания загорался, и так я испытал 2 абсолютно разных дисплея, подозреваю что они на HD44780, а подключаемые мной были с S6A0069 от Самсунга. Теперь буду знать. Еще раз благодарю за помощь!