aboutsummaryrefslogtreecommitdiffstats
path: root/src/lcd_st7920.c
blob: 4351d6ac17713f7a7d7739dead88442623c44dd6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Commands for sending messages to an st7920 lcd driver
//
// Copyright (C) 2018  Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include "basecmd.h" // oid_alloc
#include "board/gpio.h" // gpio_out_write
#include "board/irq.h" // irq_poll
#include "board/misc.h" // timer_from_us
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_SHUTDOWN

struct st7920 {
    uint32_t last_cmd_time, cmd_wait_ticks;
    struct gpio_out sclk, sid;
};


/****************************************************************
 * Transmit functions
 ****************************************************************/

#define SYNC_CMD  0xf8
#define SYNC_DATA 0xfa

// Write eight bits to the st7920 via the serial interface
static void
st7920_xmit_byte(struct st7920 *s, uint8_t data)
{
    struct gpio_out sclk = s->sclk, sid = s->sid;
    uint8_t i;
    for (i=0; i<8; i++) {
        if (data & 0x80) {
            gpio_out_toggle(sid);
            data = ~data;
        }
        gpio_out_toggle(sclk);
        data <<= 1;
        gpio_out_toggle(sclk);
    }
}

// Transmit a series of command bytes to the chip
static void
st7920_xmit(struct st7920 *s, uint8_t count, uint8_t *cmds)
{
    uint32_t last_cmd_time=s->last_cmd_time, cmd_wait_ticks=s->cmd_wait_ticks;
    while (count--) {
        uint8_t cmd = *cmds++;
        st7920_xmit_byte(s, cmd & 0xf0);
        // Can't complete transfer until delay complete
        while (timer_read_time() - last_cmd_time < cmd_wait_ticks)
            irq_poll();
        st7920_xmit_byte(s, cmd << 4);
        last_cmd_time = timer_read_time();
    }
    s->last_cmd_time = last_cmd_time;
}


/****************************************************************
 * Interface
 ****************************************************************/

void
command_config_st7920(uint32_t *args)
{
    struct st7920 *s = oid_alloc(args[0], command_config_st7920, sizeof(*s));
    s->sclk = gpio_out_setup(args[2], 0);
    s->sid = gpio_out_setup(args[3], 0);
    gpio_out_setup(args[1], 1);

    // Calibrate cmd_wait_ticks
    st7920_xmit_byte(s, SYNC_CMD);
    st7920_xmit_byte(s, 0x20);
    irq_disable();
    uint32_t start = timer_read_time();
    st7920_xmit_byte(s, 0x00);
    uint32_t end = timer_read_time();
    irq_enable();
    s->last_cmd_time = end;
    uint32_t diff = end - start, delay_ticks = args[4];
    if (delay_ticks > diff)
        s->cmd_wait_ticks = delay_ticks - diff;
}
DECL_COMMAND(command_config_st7920,
             "config_st7920 oid=%c cs_pin=%u sclk_pin=%u sid_pin=%u"
             " delay_ticks=%u");

void
command_st7920_send_cmds(uint32_t *args)
{
    struct st7920 *s = oid_lookup(args[0], command_config_st7920);
    st7920_xmit_byte(s, SYNC_CMD);
    uint8_t len = args[1], *cmds = (void*)(size_t)args[2];
    st7920_xmit(s, len, cmds);
}
DECL_COMMAND(command_st7920_send_cmds, "st7920_send_cmds oid=%c cmds=%*s");

void
command_st7920_send_data(uint32_t *args)
{
    struct st7920 *s = oid_lookup(args[0], command_config_st7920);
    st7920_xmit_byte(s, SYNC_DATA);
    uint8_t len = args[1], *data = (void*)(size_t)args[2];
    st7920_xmit(s, len, data);
}
DECL_COMMAND(command_st7920_send_data, "st7920_send_data oid=%c data=%*s");

void
st7920_shutdown(void)
{
    uint8_t i;
    struct st7920 *s;
    foreach_oid(i, s, command_config_st7920) {
        gpio_out_write(s->sclk, 0);
        gpio_out_write(s->sid, 0);
    }
}
DECL_SHUTDOWN(st7920_shutdown);