2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4 Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
9 // if this is defined we use the R/W poll mode instead of fixed delays
10 // However at the slower I2C frequency required for Viki long cables it is slower than fixed delay
11 // taken from LiquidCrystalFast.cpp and implemented for Viki LCD here by Jim Morris
12 //#define USE_FASTMODE
14 //MCP23017 - Adafruit RGB LCD Shield and VikiLCD
15 // bit pattern for the burstBits function is
17 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
18 // RS RW EN D4 D5 D6 D7 LB LG LR BZ B4 B3 B2 B1 B0
19 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
20 #define M17_BIT_RS 0x8000
21 #define M17_BIT_RW 0x4000
22 #define M17_BIT_EN 0x2000
23 #define M17_BIT_D4 0x1000
24 #define M17_BIT_D5 0x0800
25 #define M17_BIT_D6 0x0400
26 #define M17_BIT_D7 0x0200
27 #define M17_BIT_LB 0x0100
28 #define M17_BIT_LG 0x0080
29 #define M17_BIT_LR 0x0040
30 #define M17_BIT_BZ 0x0020 //Added a buzzer on this pin
31 #define M17_BIT_B4 0x0010
32 #define M17_BIT_B3 0x0008
33 #define M17_BIT_B2 0x0004
34 #define M17_BIT_B1 0x0002
35 #define M17_BIT_B0 0x0001
39 this->i2c_address
= MCP23017_ADDRESS
;
40 this->backlightval
= 0x00;
41 this->displaycontrol
= 0x00;
42 this->displayfunction
= LCD_4BITMODE
| LCD_2LINE
| LCD_5x8DOTS
; // in case they forget to call begin() at least we have somethin
43 this->displaymode
= 0x00;
47 // this->i2c = new mbed::I2C(P0_27, P0_28);
48 this->i2c
= new mbed::I2C(p9
, p10
); // P0_0, P0_1
50 i2c
->frequency(60000);
52 // configure the pins to use
53 this->encoder_a_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_a_pin_checksum
)->by_default("nc")->as_string())->as_input();
55 this->encoder_b_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_b_pin_checksum
)->by_default("nc")->as_string())->as_input();
57 this->button_pause_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, button_pause_pin_checksum
)->by_default("nc")->as_string())->as_input();
60 button_pause
.pin(&this->button_pause_pin
)->up_attach( this, &VikiLCD::on_pause_release
);
69 // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
70 // according to datasheet, we need at least 40ms after power rises above 2.7V
71 // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
76 this->displayfunction
= LCD_4BITMODE
| LCD_2LINE
| LCD_5x8DOTS
;
77 this->backlightval
= LCD_NOBACKLIGHT
;
81 // now set up input/output pins in MCP23017
82 data
[0]= MCP23017_IODIRA
;
83 data
[1]= 0x1F; // buttons input, all others output
84 i2c
->write(this->i2c_address
, data
, 2);
87 // set the button pullups
88 data
[0]= MCP23017_GPPUA
;
90 i2c
->write(this->i2c_address
, data
, 2);
92 data
[0]= MCP23017_IODIRB
;
93 data
[1]= 0x00; // all pins output
94 i2c
->write(this->i2c_address
, data
, 2);
99 //put the LCD into 4 bit mode
100 // start with a non-standard command to make it realize we're speaking 4-bit here
101 // per LCD datasheet, first command is a single 4-bit burst, 0011.
103 // we cannot assume that the LCD panel is powered at the same time as
104 // the arduino, so we have to perform a software reset as per page 45
105 // of the HD44780 datasheet - (kch)
108 // bit pattern for the burstBits function is
110 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
111 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
112 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
113 for (uint8_t i
=0;i
< 3;i
++) {
114 burstBits8b((M17_BIT_EN
|M17_BIT_D5
|M17_BIT_D4
) >> 8);
115 burstBits8b((M17_BIT_D5
|M17_BIT_D4
) >> 8);
117 burstBits8b((M17_BIT_EN
|M17_BIT_D5
) >> 8);
118 burstBits8b(M17_BIT_D5
>> 8);
121 wait_ms(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast.
123 command(LCD_FUNCTIONSET
| displayfunction
); // then send 0010NF00 (N=lines, F=font)
124 wait_ms(5); // for safe keeping...
125 command(LCD_FUNCTIONSET
| displayfunction
); // ... twice.
128 // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor.
129 displaycontrol
= (LCD_DISPLAYON
|LCD_BACKLIGHT
);
134 displaymode
= LCD_ENTRYLEFT
| LCD_ENTRYSHIFTDECREMENT
;
135 // set the entry mode
136 command(LCD_ENTRYMODESET
| displaymode
);
139 // we use this to burst bits to the GPIO chip whenever we need to. avoids repetitive code.
140 void VikiLCD::burstBits8b(uint8_t value
) {
142 data
[0] = MCP23017_GPIOB
;
144 i2c
->write(this->i2c_address
, data
, 2);
147 // value byte order is BA
148 void VikiLCD::burstBits16(uint16_t value
) {
150 data
[0] = MCP23017_GPIOA
;
153 i2c
->write(this->i2c_address
, data
, 3);
156 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
157 void VikiLCD::buzz(long duration
, uint16_t freq
) {
159 int currentRegister
= 0;
160 // read gpio register
161 data
[0] = MCP23017_GPIOA
;
162 i2c
->write(this->i2c_address
, data
, 1);
163 i2c
->read(this->i2c_address
, data
, 1); // Read from selected Register
164 currentRegister
= data
[0];
166 duration
*=1000; //convert from ms to us
167 long period
= 1000000 / freq
; // period in us
168 long elapsed_time
= 0;
169 while (elapsed_time
< duration
) {
170 data
[0]= MCP23017_GPIOA
;
171 data
[1]= currentRegister
|= M17_BIT_BZ
;
172 i2c
->write(this->i2c_address
, data
, 2);
176 data
[0]= MCP23017_GPIOA
;
177 data
[1]= currentRegister
&= ~M17_BIT_BZ
;
178 i2c
->write(this->i2c_address
, data
, 2);
181 elapsed_time
+= (period
);
185 uint8_t VikiLCD::readButtons(void) {
187 data
[0] = MCP23017_GPIOA
;
188 i2c
->write(this->i2c_address
, data
, 1);
189 i2c
->read(this->i2c_address
, data
, 1); // Read from selected Register
191 // check the button pause
192 button_pause
.check_signal();
194 return (~data
[0]) & ALL_BUTTON_BITS
;
197 int VikiLCD::readEncoderDelta() {
198 static int8_t enc_states
[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
199 static uint8_t old_AB
= 0;
200 old_AB
<<= 2; //remember previous state
201 old_AB
|= ( this->encoder_a_pin
.get() + ( this->encoder_b_pin
.get() * 2 ) ); //add current state
202 return enc_states
[(old_AB
&0x0f)];
205 void VikiLCD::clear()
207 command(LCD_CLEARDISPLAY
); // clear display, set cursor position to zero
209 wait_us(2000); // this command takes a long time!
215 command(LCD_RETURNHOME
); // set cursor position to zero
217 wait_us(2000); // this command takes a long time!
221 void VikiLCD::setCursor(uint8_t col
, uint8_t row
)
223 int row_offsets
[] = { 0x00, 0x40, 0x14, 0x54 };
224 if ( row
> _numlines
) row
= _numlines
- 1; // we count rows starting w/0
225 command(LCD_SETDDRAMADDR
| (col
+ row_offsets
[row
]));
228 // Turn the display on/off (quickly)
229 void VikiLCD::noDisplay() {
230 displaycontrol
&= ~LCD_DISPLAYON
;
231 command(LCD_DISPLAYCONTROL
| displaycontrol
);
233 void VikiLCD::display() {
234 displaycontrol
|= LCD_DISPLAYON
;
235 command(LCD_DISPLAYCONTROL
| displaycontrol
);
238 // Turns the underline cursor on/off
239 void VikiLCD::noCursor() {
240 displaycontrol
&= ~LCD_CURSORON
;
241 command(LCD_DISPLAYCONTROL
| displaycontrol
);
243 void VikiLCD::cursor() {
244 displaycontrol
|= LCD_CURSORON
;
245 command(LCD_DISPLAYCONTROL
| displaycontrol
);
248 // Turn on and off the blinking cursor
249 void VikiLCD::noBlink() {
250 displaycontrol
&= ~LCD_BLINKON
;
251 command(LCD_DISPLAYCONTROL
| displaycontrol
);
253 void VikiLCD::blink() {
254 displaycontrol
|= LCD_BLINKON
;
255 command(LCD_DISPLAYCONTROL
| displaycontrol
);
258 // These commands scroll the display without changing the RAM
259 void VikiLCD::scrollDisplayLeft(void) {
260 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVELEFT
);
262 void VikiLCD::scrollDisplayRight(void) {
263 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVERIGHT
);
266 // This is for text that flows Left to Right
267 void VikiLCD::leftToRight(void) {
268 displaymode
|= LCD_ENTRYLEFT
;
269 command(LCD_ENTRYMODESET
| displaymode
);
272 // This is for text that flows Right to Left
273 void VikiLCD::rightToLeft(void) {
274 displaymode
&= ~LCD_ENTRYLEFT
;
275 command(LCD_ENTRYMODESET
| displaymode
);
278 // This will 'right justify' text from the cursor
279 void VikiLCD::autoscroll(void) {
280 displaymode
|= LCD_ENTRYSHIFTINCREMENT
;
281 command(LCD_ENTRYMODESET
| displaymode
);
284 // This will 'left justify' text from the cursor
285 void VikiLCD::noAutoscroll(void) {
286 displaymode
&= ~LCD_ENTRYSHIFTINCREMENT
;
287 command(LCD_ENTRYMODESET
| displaymode
);
290 void VikiLCD::command(uint8_t value
) {
294 void VikiLCD::write(char value
) {
298 // Allows to set the backlight, if the LCD backpack is used
299 void VikiLCD::setBacklight(uint8_t status
) {
300 // LED turns on when bit is cleared
301 _backlightBits
= M17_BIT_LB
|M17_BIT_LG
|M17_BIT_LR
; // all off
302 if (status
& LED_RED
) _backlightBits
&= ~M17_BIT_LR
; // red on
303 if (status
& LED_GREEN
) _backlightBits
&= ~M17_BIT_LG
; // green on
304 if (status
& LED_BLUE
) _backlightBits
&= ~M17_BIT_LB
; // blue on
306 burstBits16(_backlightBits
);
309 // write either command or data, burst it to the expander over I2C.
310 void VikiLCD::send(uint8_t value
, uint8_t mode
) {
312 // polls for ready. not sure on I2C this is any faster
314 // set Data pins as input
316 data
[0]= MCP23017_IODIRB
;
318 i2c
->write(this->i2c_address
, data
, 2);
319 uint8_t b
= _backlightBits
>> 8;
320 burstBits8b((M17_BIT_RW
>>8)|b
); // RW hi,RS lo
322 data
[0] = MCP23017_GPIOB
;
324 burstBits8b(((M17_BIT_RW
|M17_BIT_EN
)>>8)|b
); // EN hi
325 i2c
->write(this->i2c_address
, data
, 1);
326 i2c
->read(this->i2c_address
, &busy
, 1); // Read D7
327 burstBits8b((M17_BIT_RW
>>8)|b
); // EN lo
328 burstBits8b(((M17_BIT_RW
|M17_BIT_EN
)>>8)|b
); // EN hi
329 burstBits8b((M17_BIT_RW
>>8)|b
); // EN lo
330 } while ((busy
&(M17_BIT_D7
>>8)) != 0);
332 // reset data bits as output
333 data
[0]= MCP23017_IODIRB
;
335 i2c
->write(this->i2c_address
, data
, 2);
336 burstBits8b(b
); // RW lo
342 // BURST SPEED, OH MY GOD
343 // the (now High Speed!) I/O expander pinout
344 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
345 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
346 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
348 // n.b. RW bit stays LOW to write
349 uint8_t buf
= _backlightBits
>> 8;
351 if (value
& 0x10) buf
|= M17_BIT_D4
>> 8;
352 if (value
& 0x20) buf
|= M17_BIT_D5
>> 8;
353 if (value
& 0x40) buf
|= M17_BIT_D6
>> 8;
354 if (value
& 0x80) buf
|= M17_BIT_D7
>> 8;
356 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
357 else buf
|= M17_BIT_EN
>> 8; // EN
361 // resend w/ EN turned off
362 buf
&= ~(M17_BIT_EN
>> 8);
366 buf
= _backlightBits
>> 8;
368 if (value
& 0x01) buf
|= M17_BIT_D4
>> 8;
369 if (value
& 0x02) buf
|= M17_BIT_D5
>> 8;
370 if (value
& 0x04) buf
|= M17_BIT_D6
>> 8;
371 if (value
& 0x08) buf
|= M17_BIT_D7
>> 8;
373 if (mode
) buf
|= (M17_BIT_RS
|M17_BIT_EN
) >> 8; // RS+EN
374 else buf
|= M17_BIT_EN
>> 8; // EN
378 // resend w/ EN turned off
379 buf
&= ~(M17_BIT_EN
>> 8);
383 // We pause the system
384 uint32_t VikiLCD::on_pause_release(uint32_t dummy
){
386 THEKERNEL
->pauser
->take();
389 THEKERNEL
->pauser
->release();