lcd.c 9.74 KB
Newer Older
1
#include <string.h>
2
#include <stm32f4xx_gpio.h>
Damien's avatar
Damien committed
3

4
#include "nlr.h"
Damien's avatar
Damien committed
5
#include "misc.h"
6
7
8
9
10
#include "mpyconfig.h"
#include "parse.h"
#include "compile.h"
#include "runtime.h"

11
12
#include "systick.h"
#include "font_petme128_8x8.h"
13
#include "lcd.h"
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

#define PYB_LCD_PORT        (GPIOA)
#define PYB_LCD_CS1_PIN     (GPIO_Pin_0)
#define PYB_LCD_RST_PIN     (GPIO_Pin_1)
#define PYB_LCD_A0_PIN      (GPIO_Pin_2)
#define PYB_LCD_SCL_PIN     (GPIO_Pin_3)
#define PYB_LCD_SI_PIN      (GPIO_Pin_4)

#define LCD_INSTR (0)
#define LCD_DATA (1)

static void lcd_out(int instr_data, uint8_t i) {
    sys_tick_delay_ms(0);
    PYB_LCD_PORT->BSRRH = PYB_LCD_CS1_PIN; // CS=0; enable
    if (instr_data == LCD_INSTR) {
        PYB_LCD_PORT->BSRRH = PYB_LCD_A0_PIN; // A0=0; select instr reg
    } else {
        PYB_LCD_PORT->BSRRL = PYB_LCD_A0_PIN; // A0=1; select data reg
    }
    // send byte bigendian, latches on rising clock
    for (uint32_t n = 0; n < 8; n++) {
        sys_tick_delay_ms(0);
        PYB_LCD_PORT->BSRRH = PYB_LCD_SCL_PIN; // SCL=0
        if ((i & 0x80) == 0) {
            PYB_LCD_PORT->BSRRH = PYB_LCD_SI_PIN; // SI=0
        } else {
            PYB_LCD_PORT->BSRRL = PYB_LCD_SI_PIN; // SI=1
        }
        i <<= 1;
        sys_tick_delay_ms(0);
        PYB_LCD_PORT->BSRRL = PYB_LCD_SCL_PIN; // SCL=1
    }
    PYB_LCD_PORT->BSRRL = PYB_LCD_CS1_PIN; // CS=1; disable

    /*
    in Python, native types:
    CS1_PIN(const) = 0
    n = int(0)
    delay_ms(0)
    PORT[word:BSRRH] = 1 << CS1_PIN
    for n in range(0, 8):
        delay_ms(0)
        PORT[word:BSRRH] = 1 << SCL_PIN
        if i & 0x80 == 0:
            PORT[word:BSRRH] = 1 << SI_PIN
        else:
            PORT[word:BSRRL] = 1 << SI_PIN
        i <<= 1
        delay_ms(0)
        PORT[word:BSRRL] = 1 << SCL_PIN
    */
}

/*
static void lcd_data_out(uint8_t i) {
    delay_ms(0);
    PYB_LCD_PORT->BSRRH = PYB_LCD_CS1_PIN; // CS=0; enable
    PYB_LCD_PORT->BSRRL = PYB_LCD_A0_PIN; // A0=1; select data reg
    // send byte bigendian, latches on rising clock
    for (uint32_t n = 0; n < 8; n++) {
        delay_ms(0);
        PYB_LCD_PORT->BSRRH = PYB_LCD_SCL_PIN; // SCL=0
        if ((i & 0x80) == 0) {
            PYB_LCD_PORT->BSRRH = PYB_LCD_SI_PIN; // SI=0
        } else {
            PYB_LCD_PORT->BSRRL = PYB_LCD_SI_PIN; // SI=1
        }
        i <<= 1;
        delay_ms(0);
        PYB_LCD_PORT->BSRRL = PYB_LCD_SCL_PIN; // SCL=1
    }
    PYB_LCD_PORT->BSRRL = PYB_LCD_CS1_PIN; // CS=1; disable
}
*/

89
90
91
92
93
94
95
96
97
98
99
100
101
102
// writes 8 vertical pixels
// pos 0 is upper left, pos 1 is 8 pixels to right of that, pos 128 is 8 pixels below that
py_obj_t lcd_draw_pixel_8(py_obj_t py_pos, py_obj_t py_val) {
    int pos = py_obj_get_int(py_pos);
    int val = py_obj_get_int(py_val);
    int page = pos / 128;
    int offset = pos - (page * 128);
    lcd_out(LCD_INSTR, 0xb0 | page); // page address set
    lcd_out(LCD_INSTR, 0x10 | ((offset >> 4) & 0x0f)); // column address set upper
    lcd_out(LCD_INSTR, 0x00 | (offset & 0x0f)); // column address set lower
    lcd_out(LCD_DATA, val); // write data
    return py_const_none;
}

103
104
105
#define LCD_BUF_W (16)
#define LCD_BUF_H (4)

106
char lcd_char_buffer[LCD_BUF_W * LCD_BUF_H];
107
108
109
110
int lcd_line;
int lcd_column;
int lcd_next_line;

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#define LCD_PIX_BUF_SIZE (128 * 32 / 8)
byte lcd_pix_buf[LCD_PIX_BUF_SIZE];
byte lcd_pix_buf2[LCD_PIX_BUF_SIZE];

py_obj_t lcd_pix_clear(void) {
    memset(lcd_pix_buf, 0, LCD_PIX_BUF_SIZE);
    memset(lcd_pix_buf2, 0, LCD_PIX_BUF_SIZE);
    return py_const_none;
}

py_obj_t lcd_pix_get(py_obj_t py_x, py_obj_t py_y) {
    int x = py_obj_get_int(py_x);
    int y = py_obj_get_int(py_y);
    if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
        uint byte_pos = x + 128 * ((uint)y >> 3);
        if (lcd_pix_buf[byte_pos] & (1 << (y & 7))) {
            return py_obj_new_int(1);
        }
    }
    return py_obj_new_int(0);
}

py_obj_t lcd_pix_set(py_obj_t py_x, py_obj_t py_y) {
    int x = py_obj_get_int(py_x);
    int y = py_obj_get_int(py_y);
    if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
        uint byte_pos = x + 128 * ((uint)y >> 3);
        lcd_pix_buf2[byte_pos] |= 1 << (y & 7);
    }
    return py_const_none;
}

py_obj_t lcd_pix_reset(py_obj_t py_x, py_obj_t py_y) {
    int x = py_obj_get_int(py_x);
    int y = py_obj_get_int(py_y);
    if (0 <= x && x <= 127 && 0 <= y && y <= 31) {
        uint byte_pos = x + 128 * ((uint)y >> 3);
        lcd_pix_buf2[byte_pos] &= ~(1 << (y & 7));
    }
    return py_const_none;
}

py_obj_t lcd_pix_show(void) {
    memcpy(lcd_pix_buf, lcd_pix_buf2, LCD_PIX_BUF_SIZE);
    for (uint page = 0; page < 4; page++) {
        lcd_out(LCD_INSTR, 0xb0 | page); // page address set
        lcd_out(LCD_INSTR, 0x10); // column address set upper; 0
        lcd_out(LCD_INSTR, 0x00); // column address set lower; 0
        for (uint i = 0; i < 128; i++) {
            lcd_out(LCD_DATA, lcd_pix_buf[i + 128 * page]);
        }
    }
    return py_const_none;
}

166
void lcd_init(void) {
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
    // set the outputs high
    PYB_LCD_PORT->BSRRL = PYB_LCD_CS1_PIN;
    PYB_LCD_PORT->BSRRL = PYB_LCD_RST_PIN;
    PYB_LCD_PORT->BSRRL = PYB_LCD_A0_PIN;
    PYB_LCD_PORT->BSRRL = PYB_LCD_SCL_PIN;
    PYB_LCD_PORT->BSRRL = PYB_LCD_SI_PIN;

    // make them push/pull outputs
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = PYB_LCD_CS1_PIN | PYB_LCD_RST_PIN | PYB_LCD_A0_PIN | PYB_LCD_SCL_PIN | PYB_LCD_SI_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(PYB_LCD_PORT, &GPIO_InitStructure);

    // init the LCD
    sys_tick_delay_ms(1); // wait a bit
    PYB_LCD_PORT->BSRRH = PYB_LCD_RST_PIN; // RST=0; reset
    sys_tick_delay_ms(1); // wait for reset; 2us min
    PYB_LCD_PORT->BSRRL = PYB_LCD_RST_PIN; // RST=1; enable
    sys_tick_delay_ms(1); // wait for reset; 2us min
    lcd_out(LCD_INSTR, 0xa0); // ADC select, normal
    lcd_out(LCD_INSTR, 0xc8); // common output mode select, reverse
    lcd_out(LCD_INSTR, 0xa2); // LCD bias set, 1/9 bias
    lcd_out(LCD_INSTR, 0x2f); // power control set, 0b111=(booster on, vreg on, vfollow on)
    lcd_out(LCD_INSTR, 0x21); // v0 voltage regulator internal resistor ratio set, 0b001=small
    lcd_out(LCD_INSTR, 0x81); // electronic volume mode set
    lcd_out(LCD_INSTR, 0x34); // electronic volume register set, 0b110100
    lcd_out(LCD_INSTR, 0x40); // display start line set, 0
    lcd_out(LCD_INSTR, 0xaf); // LCD display, on

    // clear display
    for (int page = 0; page < 4; page++) {
        lcd_out(LCD_INSTR, 0xb0 | page); // page address set
        lcd_out(LCD_INSTR, 0x10); // column address set upper
        lcd_out(LCD_INSTR, 0x00); // column address set lower
        for (int i = 0; i < 128; i++) {
            lcd_out(LCD_DATA, 0x00);
        }
    }

    for (int i = 0; i < LCD_BUF_H * LCD_BUF_W; i++) {
210
        lcd_char_buffer[i] = ' ';
211
212
213
214
    }
    lcd_line = 0;
    lcd_column = 0;
    lcd_next_line = 0;
215
216
217
218
219
220
221
222
223
224

    // Python interface
    py_obj_t m = py_module_new();
    rt_store_attr(m, qstr_from_str_static("lcd8"), rt_make_function_2(lcd_draw_pixel_8));
    rt_store_attr(m, qstr_from_str_static("clear"), rt_make_function_0(lcd_pix_clear));
    rt_store_attr(m, qstr_from_str_static("get"), rt_make_function_2(lcd_pix_get));
    rt_store_attr(m, qstr_from_str_static("set"), rt_make_function_2(lcd_pix_set));
    rt_store_attr(m, qstr_from_str_static("reset"), rt_make_function_2(lcd_pix_reset));
    rt_store_attr(m, qstr_from_str_static("show"), rt_make_function_0(lcd_pix_show));
    rt_store_name(qstr_from_str_static("lcd"), m);
225
226
}

227
228
229
230
void lcd_print_str(const char *str) {
    lcd_print_strn(str, strlen(str));
}

231
232
233
234
235
236
237
238
239
240
241
242
void lcd_print_strn(const char *str, unsigned int len) {
    int redraw_min = lcd_line * LCD_BUF_W + lcd_column;
    int redraw_max = redraw_min;
    int did_new_line = 0;
    for (; len > 0; len--, str++) {
        // move to next line if needed
        if (lcd_next_line) {
            if (lcd_line + 1 < LCD_BUF_H) {
                lcd_line += 1;
            } else {
                lcd_line = LCD_BUF_H - 1;
                for (int i = 0; i < LCD_BUF_W * (LCD_BUF_H - 1); i++) {
243
                    lcd_char_buffer[i] = lcd_char_buffer[i + LCD_BUF_W];
244
245
                }
                for (int i = 0; i < LCD_BUF_W; i++) {
246
                    lcd_char_buffer[LCD_BUF_W * (LCD_BUF_H - 1) + i] = ' ';
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
                }
                redraw_min = 0;
                redraw_max = LCD_BUF_W * LCD_BUF_H;
            }
            lcd_next_line = 0;
            lcd_column = 0;
            did_new_line = 1;
        }
        if (*str == '\n') {
            lcd_next_line = 1;
        } else if (lcd_column >= LCD_BUF_W) {
            lcd_next_line = 1;
            str -= 1;
            len += 1;
        } else {
262
            lcd_char_buffer[lcd_line * LCD_BUF_W + lcd_column] = *str;
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
            lcd_column += 1;
            int max = lcd_line * LCD_BUF_W + lcd_column;
            if (max > redraw_max) {
                redraw_max = max;
            }
        }
    }

    int last_page = -1;
    for (int i = redraw_min; i < redraw_max; i++) {
        int page = i / LCD_BUF_W;
        if (page != last_page) {
            int offset = 8 * (i - (page * LCD_BUF_W));
            lcd_out(LCD_INSTR, 0xb0 | page); // page address set
            lcd_out(LCD_INSTR, 0x10 | ((offset >> 4) & 0x0f)); // column address set upper
            lcd_out(LCD_INSTR, 0x00 | (offset & 0x0f)); // column address set lower
            last_page = page;
        }
281
        int chr = lcd_char_buffer[i];
282
283
284
285
        if (chr < 32 || chr > 126) {
            chr = 127;
        }
        const uint8_t *chr_data = &font_petme128_8x8[(chr - 32) * 8];
286
287
        for (int j = 0; j < 8; j++) {
            lcd_out(LCD_DATA, chr_data[j]);
288
289
290
291
292
293
294
        }
    }

    if (did_new_line) {
        sys_tick_delay_ms(200);
    }
}