summaryrefslogtreecommitdiff
path: root/multiplexer.c
blob: c893ca37513bc530634923775c3bd7e06f98a51b (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
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
166
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#ifndef SKIP_GPIO
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#endif // SKIP_GPIO
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>

#include "multiplexer.h"

int  mem_fd;
void *gpio_map;
volatile unsigned *gpio;

//
// Set up a memory regions to access GPIO
//
int multiplexer_setup_root()
{
#ifndef SKIP_GPIO
  /* open /dev/mem */
  if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
    perror("can't open /dev/mem");
    return EXIT_FAILURE;
  }

  /* mmap GPIO */
  gpio_map = mmap(
    NULL,             //Any adddress in our space will do
    BLOCK_SIZE,       //Map length
    PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
    MAP_SHARED,       //Shared with other processes
    mem_fd,           //File to map
    GPIO_BASE         //Offset to GPIO peripheral
  );

  close(mem_fd); //No need to keep mem_fd open after mmap

  if (gpio_map == MAP_FAILED) {
    perror("mmap error");
    return EXIT_FAILURE;
  }

  // Always use volatile pointer!
  gpio = (volatile unsigned *)gpio_map;
#endif // !SKIP_GPIO
} // multiplexer_setup_root

//
// Shift the bits provided in content into the display
//
void *put_on_display(void *param)
{
  t_display_data *display_data = param;
  int line, column;
#ifndef SKIP_GPIO
  struct timespec start_time, stop_time;
#endif // !SKIP_GPIO

  while (display_data -> keep_running) {

#ifdef SKIP_GPIO
    usleep(10000);
#endif // SKIP_GPIO
    display_data -> is_buf = display_data -> should_buf;
    if (!display_data -> should_be_on) {
      if (display_data -> is_on) {
        turn_off_display();
        memset(display_data -> buf, 0, sizeof(display_data -> buf));
        display_data -> is_on = 0;
      }
      sleep(1);
      continue;
    }
    display_data -> is_on = 1;
#ifdef SKIP_GPIO
#ifndef SILENT
    printf("=\n");
#endif // !SILENT
    for (line=0; line<7; line++) {
      for (column=0; column<40; column++) {
#ifndef SILENT
        if (display_data -> buf[display_data -> is_buf][column] & (0x01 << line))
          printf("X");
        else
          printf(" ");
#endif // !SILENT
#else // SKIP_GPIO
    for (line=6; line>=0; line--) {
      GPIO_CLR = 1<<GATE_PIN; // Licht an
      if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time) != 0) {
        display_data -> keep_running = 0;
        perror("clock_gettime failed");
        break;
      }
      for (column=0; column<8; column++) {
        GPIO_ALTER(column + line == 7) = 1<<SER_DAT_PIN;
        wait_tpic_settle_time
        GPIO_CLR = 1<<SER_CLK_PIN;
        wait_tpic_settle_time
        GPIO_SET = 1<<SER_CLK_PIN;
        wait_tpic_settle_time
      }
      for (column=39; column>=0; column--) {
        GPIO_ALTER(display_data -> buf[display_data -> is_buf][column] & (0x01 << line)) = 1<<SER_DAT_PIN;
        wait_tpic_settle_time
        GPIO_CLR = 1<<SER_CLK_PIN;
        wait_tpic_settle_time
        GPIO_SET = 1<<SER_CLK_PIN;
        wait_tpic_settle_time
#endif // SKIP_GPIO
      }
#ifdef SKIP_GPIO
#ifndef SILENT
      printf("\n");
#endif // !SILENT
#else // SKIP_GPIO
      if (clock_gettime(CLOCK_MONOTONIC_RAW, &stop_time) != 0) {
        display_data -> keep_running = 0;
        perror("clock_gettime failed");
        break;
      }
      // adjust tpic_settle_time_iterator
      if ((tpic_settle_time_iterator > 0) && (stop_time.tv_sec - start_time.tv_sec + (stop_time.tv_nsec - start_time.tv_nsec) / 1000000000. > 0.0003)) {
//        fprintf(stderr, "%d -> ", tpic_settle_time_iterator);
        tpic_settle_time_iterator--;
//        fprintf(stderr, "%d\n", tpic_settle_time_iterator);
      }
      else if ((tpic_settle_time_iterator < 200000) && (stop_time.tv_sec - start_time.tv_sec + (stop_time.tv_nsec - start_time.tv_nsec) / 1000000000. < 0.0001)) {
//        fprintf(stderr, "%d -> ", tpic_settle_time_iterator);
        tpic_settle_time_iterator++;
        tpic_settle_time_iterator *= 2;
//        fprintf(stderr, "%d\n", tpic_settle_time_iterator);
      }
      // wait until start_time + 1ms
      int wait = (start_time.tv_sec - stop_time.tv_sec) * 1000000 + (start_time.tv_nsec - stop_time.tv_nsec) / 1000 + 1000;
      if (wait > 0)
        usleep(wait);
      GPIO_SET = 1<<GATE_PIN; // Licht aus
      wait_tpic_settle_time
      GPIO_CLR = 1<<PAR_CLK_PIN;
      wait_tpic_settle_time
      GPIO_SET = 1<<PAR_CLK_PIN;
      wait_tpic_settle_time
#endif // SKIP_GPIO
    }
  }
  return NULL;
} // put_on_display

// scroll the text over the display
void *scroll_it(void *param)
{
  t_scroll_data *scroll_data = (t_scroll_data *) param;
  t_scroll_buffer buf[3];
  for (int i=0; i<3; i++)
    memset(&buf[i], 0, sizeof(buf[i]));
  t_display_data display_data;
  multiplexer_setup_non_root(&display_data);
  int is_buf = 0;
  int column = 0;
  /**
   * ein Aufruf von clock_gettime(CLOCK_MONOTONIC_RAW, ...) braucht
   * etwa 2.2 µs auf dem Raspi1
  **/
  struct timespec start_time, stop_time;

  while (scroll_data -> keep_running) {
    if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time) != 0) {
      scroll_data -> keep_running = 0;
      perror("clock_gettime failed");
      break;
    }
    if ((scroll_data -> shine_until != 0) && (scroll_data -> shine_until < time(NULL))) {
      // turn off display
      display_data . should_be_on = 0;
      column = 0;
    }
    else {
      // turn on display
      display_data . should_be_on = 1;
    }
    if (scroll_data -> update) {
      if ((buf[is_buf] . len != scroll_data -> input_len) || (memcmp(&buf[is_buf] . buf[buf[is_buf] . start], scroll_data -> input, scroll_data -> input_len) != 0)) {
        is_buf = (is_buf + 1) % 2;
        // copy the currently visible, old content
        memcpy(buf[is_buf] . buf, &buf[(is_buf + 1) % 2] . buf[column + 1], 39);
        buf[is_buf] . start = 39;
        // set the length of the new content
        buf[is_buf] . len = scroll_data -> input_len;
        // copy the new content from input
        for (int i=0; i<2; i++)
          memcpy(
            &buf[is_buf] . buf[39 + i * buf[is_buf] . len],
            scroll_data -> input,
            buf[is_buf] . len
          );
        column = 0;
      }
      // clear update flag
      scroll_data -> update = 0;
    }
    if (display_data . should_be_on != 0) {
      for (int i=0; i<40; i++)
        display_data . buf[(display_data . should_buf + 1) % 3][i] =
          buf[is_buf] . buf[column + i];
      column++;
      if (buf[is_buf] . len > 0)
        while (column >= buf[is_buf] . start + buf[is_buf] . len)
          column -= buf[is_buf] . len;
    }
    if (clock_gettime(CLOCK_MONOTONIC_RAW, &stop_time) != 0) {
      scroll_data -> keep_running = 0;
      perror("clock_gettime failed");
      break;
    }
    // wait until start_time + 50ms
    int wait = (start_time.tv_sec - stop_time.tv_sec) * 1000000 + (start_time.tv_nsec - stop_time.tv_nsec) / 1000 + 25000;
    if (wait > 0)
      usleep(wait);
    if (display_data . should_be_on != 0)
      display_data . should_buf = (display_data . should_buf + 1) % 3;
  }
  display_data . keep_running = 0;
  pthread_join(display_data . thread_id, NULL);
} // scroll_it

void multiplexer_setup_non_root(t_display_data *display_data)
{
#ifndef SKIP_GPIO
  // must use INP_GPIO before we can use OUT_GPIO
  INP_GPIO(SER_DAT_PIN);
  OUT_GPIO(SER_DAT_PIN);
  INP_GPIO(SER_CLK_PIN);
  OUT_GPIO(SER_CLK_PIN);
  INP_GPIO(GATE_PIN);
  OUT_GPIO(GATE_PIN);
  INP_GPIO(PAR_CLK_PIN);
  OUT_GPIO(PAR_CLK_PIN);
  INP_GPIO(SENSE_PIN);
  // enable pull-up on SENSE_PIN
  /** adopted from wiringPi's wiringPi/wiringPi.c lines 1507-1511 **/
  *(gpio + GPPUD) = 2; // bit 0: pull down; bit 1: pull up
  usleep(5);
  *(gpio + gpioToPUDCLK[SENSE_PIN]) = 1 << (SENSE_PIN & 31);
  usleep(5);
  *(gpio + GPPUD) = 0;
  usleep(5);
  *(gpio + gpioToPUDCLK[SENSE_PIN]) = 0;
  usleep(5);
  /** end of adoption **/
#endif // !SKIP_GPIO

  for (int i=0; i<3; i++)
    memset(display_data -> buf[i],0,40);
  display_data -> is_buf = 0;
  display_data -> should_buf = 0;
  display_data -> keep_running = 1;
  display_data -> is_on = 1;
  display_data -> should_be_on = 1;
  pthread_create(&display_data -> thread_id, NULL, put_on_display, display_data);
  return;
} // multiplexer_setup_non_root

void multiplexer_setup_scroll(t_scroll_data *scroll_data)
{
  scroll_data -> update = 0;
  scroll_data -> keep_running = 1;
  scroll_data -> shine_until = 0;
  pthread_create(&scroll_data -> thread_id, NULL, scroll_it, scroll_data);
  return;
} // multiplexer_setup_scroll

void turn_off_display()
{
#ifndef SKIP_GPIO
  GPIO_SET = 1<<GATE_PIN; // Licht aus
  GPIO_CLR = 1<<SER_DAT_PIN;
  for (int i=0; i<48; i++) {
    wait_tpic_settle_time
    GPIO_CLR = 1<<SER_CLK_PIN;
    wait_tpic_settle_time
    GPIO_SET = 1<<SER_CLK_PIN;
  }
  wait_tpic_settle_time
  GPIO_CLR = 1<<PAR_CLK_PIN;
  wait_tpic_settle_time
  GPIO_SET = 1<<PAR_CLK_PIN;
#endif // !SKIP_GPIO
} // turn_off_display

int got_input()
{
#ifdef SKIP_GPIO
  struct timeval tv = { 0L, 0L };
  fd_set fds;
  FD_ZERO(&fds);
  FD_SET(0, &fds);
  if (select(1, &fds, NULL, NULL, &tv)) {
    unsigned char c;
    read(0, &c, sizeof(c));
    return 1;
  }
  else
    return 0;
#else // SKIP_GPIO
  return ! GET_GPIO(SENSE_PIN); // low = active
#endif // SKIP_GPIO
} // got_input