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/>.
7 #include "Smoothiepanel.h"
8 #include "smoothiepanel/LCDBang.h"
10 #define LCD_WRITE 0x00
14 Smoothiepanel::Smoothiepanel() {
16 this->backlightval
= 0x00;
17 this->displaycontrol
= 0x00;
18 this->displayfunction
= LCD_4BITMODE
| LCD_2LINE
| LCD_5x8DOTS
; // in case they forget to call begin() at least we have somethin
19 this->displaymode
= 0x00;
23 int i2c_pins
= THEKERNEL
->config
->value(panel_checksum
, i2c_pins_checksum
)->by_default(3)->as_number();
25 this->i2c
= new mbed::I2C(P0_0
, P0_1
);
26 }else if(i2c_pins
== 1){
27 this->i2c
= new mbed::I2C(P0_10
, P0_11
);
28 }else if(i2c_pins
== 2){
29 this->i2c
= new mbed::I2C(P0_19
, P0_20
);
31 this->i2c
= new mbed::I2C(P0_27
, P0_28
);
34 this->i2c_address
= (char)THEKERNEL
->config
->value(panel_checksum
, i2c_address_checksum
)->by_default(0)->as_number();
35 this->i2c_address
= (this->i2c_address
& 0x07) << 1;
36 this->i2c_frequency
= THEKERNEL
->config
->value(panel_checksum
, i2c_frequency_checksum
)->by_default(20000)->as_number();
37 i2c
->frequency(this->i2c_frequency
);
38 this->lcd_contrast
= THEKERNEL
->config
->value(panel_checksum
, lcd_contrast_checksum
)->by_default(0)->as_number();
40 // this->interrupt_pin.from_string(THEKERNEL->config->value(panel_checksum, i2c_interrupt_pin_checksum)->by_default("nc")->as_string())->as_input();
41 this->encoder_a_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_a_pin_checksum
)->by_default("nc")->as_string())->as_input();
42 this->encoder_b_pin
.from_string(THEKERNEL
->config
->value( panel_checksum
, encoder_b_pin_checksum
)->by_default("nc")->as_string())->as_input();
47 Smoothiepanel::~Smoothiepanel() {
51 void ledbang_init(I2C i2c
, int address
){
52 const int leds
= PCA9634_ADDRESS
| (address
& 0x0E);
55 // initialize led controller
58 i2c
.write(leds
, cmd
, 2);
61 i2c
.write(leds
, cmd
, 2);
64 i2c
.write(leds
, cmd
, 2);
66 i2c
.write(leds
, cmd
, 2);
69 cmd
[0] = 0x02; // play
71 i2c
.write(leds
, cmd
, 2);
72 cmd
[0] = 0x03; // back
74 i2c
.write(leds
, cmd
, 2);
75 cmd
[0] = 0x04; // encoder red
77 i2c
.write(leds
, cmd
, 2);
78 cmd
[0] = 0x05; // encoder green
80 i2c
.write(leds
, cmd
, 2);
81 cmd
[0] = 0x06; // encoder blue
83 i2c
.write(leds
, cmd
, 2);
84 cmd
[0] = 0x07; // lcd blue
86 i2c
.write(leds
, cmd
, 2);
87 cmd
[0] = 0x08; // lcd green
89 i2c
.write(leds
, cmd
, 2);
90 cmd
[0] = 0x09; // lcd red
92 i2c
.write(leds
, cmd
, 2);
95 void Smoothiepanel::init(){
96 // init lcd and buzzer
97 lcdbang_init(*this->i2c
);
98 // lcdbang_print(*this->i2c, " Smoothiepanel Beta - design by Logxen -");
99 lcdbang_contrast(*this->i2c
, this->lcd_contrast
);
101 ledbang_init(*this->i2c
, this->i2c_address
);
106 // cycle the buzzer pin at a certain frequency (hz) for a certain duration (ms)
107 void Smoothiepanel::buzz(long duration
, uint16_t freq
) {
108 const int expander
= PCA9505_ADDRESS
| this->i2c_address
;
112 // save register state
114 this->i2c
->write(expander
, cmd
, 1, false);
115 this->i2c
->read(expander
, cmd
, 1);
120 cmd
[1] = saved
& 0xFE;
121 this->i2c
->write(expander
, cmd
, 2);
122 wait_ms(duration
); // TODO: Make this not hold up the whole system
124 this->i2c
->write(expander
, cmd
, 2);
127 uint8_t Smoothiepanel::readButtons(void) {
128 const int expander
= PCA9505_ADDRESS
| this->i2c_address
;
129 uint8_t button_bits
= 0x00;
133 this->i2c
->write(expander
, cmd
, 1, false);
134 this->i2c
->read(expander
, cmd
, 1);
136 if((cmd
[0] & 0x10) > 0) button_bits
|= BUTTON_SELECT
;
137 if((cmd
[0] & 0x02) > 0) button_bits
|= BUTTON_LEFT
; // back button
138 // if((cmd[0] & 0x01) > 0) button_bits |= BUTTON_AUX1; // play button
140 // check the button pause
141 // button_pause.check_signal();
142 // this->setCursor(6, 4);
143 // this->printf("Buttons: 0x%02X", button_bits);
148 int Smoothiepanel::readEncoderDelta() {
149 static int8_t enc_states
[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
150 static uint8_t old_AB
= 0;
151 old_AB
<<= 2; //remember previous state
152 old_AB
|= ( this->encoder_a_pin
.get() + ( this->encoder_b_pin
.get() * 2 ) ); //add current state
153 return enc_states
[(old_AB
&0x0f)];
156 void Smoothiepanel::clear()
158 command(LCD_CLEARDISPLAY
); // clear display, set cursor position to zero
159 //#ifndef USE_FASTMODE
160 // wait_ms(50); // this command takes a long time!
164 void Smoothiepanel::home()
166 command(LCD_RETURNHOME
); // set cursor position to zero
167 //#ifndef USE_FASTMODE
168 // wait_us(2000); // this command takes a long time!
172 void Smoothiepanel::setCursor(uint8_t col
, uint8_t row
)
174 int row_offsets
[] = { 0x00, 0x40, 0x14, 0x54 };
175 if ( row
> _numlines
) row
= _numlines
- 1; // we count rows starting w/0
176 command(LCD_SETDDRAMADDR
| (col
+ row_offsets
[row
]));
179 // Turn the display on/off (quickly)
180 void Smoothiepanel::noDisplay() {
181 displaycontrol
&= ~LCD_DISPLAYON
;
182 command(LCD_DISPLAYCONTROL
| displaycontrol
);
185 void Smoothiepanel::display() {
186 displaycontrol
|= LCD_DISPLAYON
;
187 command(LCD_DISPLAYCONTROL
| displaycontrol
);
190 // Turns the underline cursor on/off
191 void Smoothiepanel::noCursor() {
192 displaycontrol
&= ~LCD_CURSORON
;
193 command(LCD_DISPLAYCONTROL
| displaycontrol
);
195 void Smoothiepanel::cursor() {
196 displaycontrol
|= LCD_CURSORON
;
197 command(LCD_DISPLAYCONTROL
| displaycontrol
);
200 // Turn on and off the blinking cursor
201 void Smoothiepanel::noBlink() {
202 displaycontrol
&= ~LCD_BLINKON
;
203 command(LCD_DISPLAYCONTROL
| displaycontrol
);
205 void Smoothiepanel::blink() {
206 displaycontrol
|= LCD_BLINKON
;
207 command(LCD_DISPLAYCONTROL
| displaycontrol
);
210 // These commands scroll the display without changing the RAM
211 void Smoothiepanel::scrollDisplayLeft(void) {
212 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVELEFT
);
214 void Smoothiepanel::scrollDisplayRight(void) {
215 command(LCD_CURSORSHIFT
| LCD_DISPLAYMOVE
| LCD_MOVERIGHT
);
218 // This is for text that flows Left to Right
219 void Smoothiepanel::leftToRight(void) {
220 displaymode
|= LCD_ENTRYLEFT
;
221 command(LCD_ENTRYMODESET
| displaymode
);
224 // This is for text that flows Right to Left
225 void Smoothiepanel::rightToLeft(void) {
226 displaymode
&= ~LCD_ENTRYLEFT
;
227 command(LCD_ENTRYMODESET
| displaymode
);
230 // This will 'right justify' text from the cursor
231 void Smoothiepanel::autoscroll(void) {
232 displaymode
|= LCD_ENTRYSHIFTINCREMENT
;
233 command(LCD_ENTRYMODESET
| displaymode
);
236 // This will 'left justify' text from the cursor
237 void Smoothiepanel::noAutoscroll(void) {
238 displaymode
&= ~LCD_ENTRYSHIFTINCREMENT
;
239 command(LCD_ENTRYMODESET
| displaymode
);
242 void Smoothiepanel::command(uint8_t value
) {
243 lcdbang_write(*this->i2c
, value
>>4, true);
244 lcdbang_write(*this->i2c
, value
, true);
247 void Smoothiepanel::write(char value
) {
248 lcdbang_write(*this->i2c
, value
);
251 // Allows to set the backlight, if the LCD backpack is used
252 void Smoothiepanel::setBacklight(uint8_t status
) {
253 /* // LED turns on when bit is cleared
254 _backlightBits = M17_BIT_LB|M17_BIT_LG|M17_BIT_LR; // all off
255 if (status & LED_RED) _backlightBits &= ~M17_BIT_LR; // red on
256 if (status & LED_GREEN) _backlightBits &= ~M17_BIT_LG; // green on
257 if (status & LED_BLUE) _backlightBits &= ~M17_BIT_LB; // blue on
259 burstBits16(_backlightBits);
263 // write either command or data, burst it to the expander over I2C.
264 void Smoothiepanel::send(uint8_t value, uint8_t mode) {
266 // polls for ready. not sure on I2C this is any faster
268 // set Data pins as input
270 data[0]= MCP23017_IODIRB;
272 i2c->write(this->i2c_address, data, 2);
273 uint8_t b= _backlightBits >> 8;
274 burstBits8b((M17_BIT_RW>>8)|b); // RW hi,RS lo
276 data[0] = MCP23017_GPIOB;
278 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
279 i2c->write(this->i2c_address, data, 1);
280 i2c->read(this->i2c_address, &busy, 1); // Read D7
281 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
282 burstBits8b(((M17_BIT_RW|M17_BIT_EN)>>8)|b); // EN hi
283 burstBits8b((M17_BIT_RW>>8)|b); // EN lo
284 } while ((busy&(M17_BIT_D7>>8)) != 0);
286 // reset data bits as output
287 data[0]= MCP23017_IODIRB;
289 i2c->write(this->i2c_address, data, 2);
290 burstBits8b(b); // RW lo
296 // BURST SPEED, OH MY GOD
297 // the (now High Speed!) I/O expander pinout
298 // B7 B6 B5 B4 B3 B2 B1 B0 A7 A6 A5 A4 A3 A2 A1 A0 - MCP23017
299 // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
300 // RS RW EN D4 D5 D6 D7 B G R B4 B3 B2 B1 B0
302 // n.b. RW bit stays LOW to write
303 uint8_t buf = _backlightBits >> 8;
305 if (value & 0x10) buf |= M17_BIT_D4 >> 8;
306 if (value & 0x20) buf |= M17_BIT_D5 >> 8;
307 if (value & 0x40) buf |= M17_BIT_D6 >> 8;
308 if (value & 0x80) buf |= M17_BIT_D7 >> 8;
310 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
311 else buf |= M17_BIT_EN >> 8; // EN
315 // resend w/ EN turned off
316 buf &= ~(M17_BIT_EN >> 8);
320 buf = _backlightBits >> 8;
322 if (value & 0x01) buf |= M17_BIT_D4 >> 8;
323 if (value & 0x02) buf |= M17_BIT_D5 >> 8;
324 if (value & 0x04) buf |= M17_BIT_D6 >> 8;
325 if (value & 0x08) buf |= M17_BIT_D7 >> 8;
327 if (mode) buf |= (M17_BIT_RS|M17_BIT_EN) >> 8; // RS+EN
328 else buf |= M17_BIT_EN >> 8; // EN
332 // resend w/ EN turned off
333 buf &= ~(M17_BIT_EN >> 8);
337 // We pause the system
338 uint32_t Smoothiepanel::on_pause_release(uint32_t dummy){
340 THEKERNEL->pauser->take();
343 THEKERNEL->pauser->release();