FabGL
ESP32 Display Controller and Graphics Library
PIT8253.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3. Feel free to use FabGL in free software and hardware:
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 #include "PIT8253.h"
28 
29 
30 
31 namespace fabgl {
32 
33 
34 
35 PIT8253::PIT8253()
36 {
37  m_mutex = xSemaphoreCreateMutex();
38 }
39 
40 
41 PIT8253::~PIT8253()
42 {
43  vSemaphoreDelete(m_mutex);
44 }
45 
46 
47 // because vTaskDelay has a resolution of 1ms, updatesPerSec should be <=1000
48 void PIT8253::runAutoTick(int freq, int updatesPerSec)
49 {
50  m_autoTickFreq = freq;
51  m_updatesPerSec = updatesPerSec;
52  xTaskCreatePinnedToCore(&autoTickTask, "", 2048, this, 5, &m_taskHandle, CoreUsage::busiestCore());
53 }
54 
55 
56 void PIT8253::autoTickTask(void * pvParameters)
57 {
58  auto p = (PIT8253*)pvParameters;
59 
60  const int ticks = p->m_autoTickFreq / p->m_updatesPerSec;
61  const int delay_ms = 1000 / p->m_updatesPerSec / portTICK_PERIOD_MS;
62  //const int delay_us = 1000000 / p->m_updatesPerSec;
63 
64  while (true) {
65  p->tick(ticks);
66  vTaskDelay(delay_ms);
67  //ets_delay_us(delay_us);
68  }
69 }
70 
71 
72 void PIT8253::reset()
73 {
74  AutoSemaphore autoSemaphore(m_mutex);
75  m_timer[0] = { false, 3, 3, 0, 0, 0, -1, true, false, false, false };
76  m_timer[1] = { false, 3, 3, 0, 0, 0, -1, true, false, false, false };
77  m_timer[2] = { false, 3, 3, 0, 0, 0, -1, true, false, false, false };
78 }
79 
80 
81 void PIT8253::write(int reg, uint8_t value)
82 {
83  AutoSemaphore autoSemaphore(m_mutex);
84 
85  int timerIndex;
86 
87  if (reg == 3) {
88 
89  // write control register
90  timerIndex = (value >> 6) & 0x03;
91 
92  int RLMode = (value >> 4) & 0x03;
93  if (RLMode == 0) {
94  // counter latching operation (doesn't change BCD or mode)
95  m_timer[timerIndex].latch = m_timer[timerIndex].count;
96  m_timer[timerIndex].LSBToggle = true;
97  } else {
98  // read/load
99  m_timer[timerIndex].mode = (value >> 1) & 0x07;
100  m_timer[timerIndex].BCD = (value & 1) == 1;
101  m_timer[timerIndex].RLMode = RLMode;
102  if (RLMode == 3)
103  m_timer[timerIndex].LSBToggle = true;
104  }
105 
106  //printf("PIT8253: write ctrl reg, %02X (timer=%d, mode=%d)\n", value, timerIndex, m_timer[timerIndex].mode);
107  if (value >> 6 == 3)
108  printf("8253, read back. Required 8254?\n");
109 
110  } else {
111 
112  // write timers registers
113  timerIndex = reg;
114  bool writeLSB = false;
115 
116  switch (m_timer[timerIndex].RLMode) {
117  case 1:
118  writeLSB = true;
119  break;
120  case 3:
121  writeLSB = m_timer[timerIndex].LSBToggle;
122  m_timer[timerIndex].LSBToggle = !m_timer[timerIndex].LSBToggle;
123  break;
124  }
125 
126  if (writeLSB) {
127  // LSB
128  //printf("PIT8253: write LSB, %02X\n", value);
129  m_timer[timerIndex].resetHolding = (m_timer[timerIndex].resetHolding & 0xFF00) | value;
130  } else {
131  // MSB
132  //printf("PIT8253: write MSB, %02X\n", value);
133  m_timer[timerIndex].resetHolding = (m_timer[timerIndex].resetHolding & 0x00FF) | (((int)value) << 8);
134  m_timer[timerIndex].resetCount = m_timer[timerIndex].resetHolding;
135  if (m_timer[timerIndex].mode == 0) {
136  m_timer[timerIndex].count = (uint16_t)(m_timer[timerIndex].resetCount - 1);
137  }
138  }
139 
140  // OUT: with mode 0 it starts low, other modes it starts high
141  changeOut(timerIndex, m_timer[timerIndex].mode != 0);
142 
143  }
144 
145 }
146 
147 
148 uint8_t PIT8253::read(int reg)
149 {
150  AutoSemaphore autoSemaphore(m_mutex);
151 
152  uint8_t value = 0;
153 
154  if (reg < 3) {
155  // read timers registers
156  int timerIndex = reg;
157 
158  int readValue = m_timer[timerIndex].latch != -1 ? m_timer[timerIndex].latch : m_timer[timerIndex].count;
159 
160  bool readLSB = false;
161  if (m_timer[timerIndex].RLMode == 1) {
162  readLSB = true;
163  } else if (m_timer[timerIndex].RLMode == 3) {
164  readLSB = m_timer[timerIndex].LSBToggle;
165  m_timer[timerIndex].LSBToggle = !m_timer[timerIndex].LSBToggle;
166  }
167 
168  if (readLSB) {
169  value = readValue & 0xFF;
170  } else {
171  value = (readValue >> 8) & 0xFF;
172  m_timer[timerIndex].latch = -1;
173  }
174  }
175 
176  // this avoids that two timer reads returns the same value (causing division by zero) maybe because the update time is still not expired.
177  // Unfortunately makes the timers less precise!
178  unsafeTick(1);
179 
180  return value;
181 }
182 
183 
184 void PIT8253::setGate(int timerIndex, bool value)
185 {
186  AutoSemaphore autoSemaphore(m_mutex);
187  switch (m_timer[timerIndex].mode) {
188  case 0:
189  case 2:
190  case 3:
191  // running when gate is high
192  m_timer[timerIndex].running = value;
193  break;
194  case 1:
195  case 5:
196  // switch to running when gate changes to high
197  if (m_timer[timerIndex].gate == false && value == true)
198  m_timer[timerIndex].running = true;
199  break;
200  }
201  m_timer[timerIndex].gate = value;
202 }
203 
204 
205 void PIT8253::changeOut(int timer, bool value)
206 {
207  if (value != m_timer[timer].out) {
208  //printf("timer %d, out = %d\n", timer, value);
209  m_timer[timer].out = value;
210  m_changeOut(m_context, timer);
211  }
212 }
213 
214 
215 void PIT8253::tick(int ticks)
216 {
217  AutoSemaphore autoSemaphore(m_mutex);
218  unsafeTick(ticks);
219  m_tick(m_context, ticks);
220 }
221 
222 
223 void PIT8253::unsafeTick(int ticks)
224 {
225  for (int timerIndex = 0; timerIndex < 3; ++timerIndex) {
226 
227  if (m_timer[timerIndex].running) {
228 
229  // modes 4 or 5, end of ending low pulse?
230  if (m_timer[timerIndex].mode >= 4 && m_timer[timerIndex].out == false) {
231  // mode 4, end of low pulse
232  changeOut(timerIndex, true);
233  m_timer[timerIndex].running = false;
234  m_timer[timerIndex].count = 65535;
235  continue;
236  }
237 
238  m_timer[timerIndex].count -= ticks;
239 
240  // in mode 3 each tick subtract 2 instead of 1
241  if (m_timer[timerIndex].mode == 3)
242  m_timer[timerIndex].count -= ticks;
243 
244  if (m_timer[timerIndex].count <= 0) {
245  // count terminated
246  if (m_timer[timerIndex].resetCount == 0) {
247  m_timer[timerIndex].count += 65536;
248  } else {
249  m_timer[timerIndex].count += m_timer[timerIndex].resetCount;
250  }
251  switch (m_timer[timerIndex].mode) {
252  case 0:
253  // at the end OUT goes high
254  changeOut(timerIndex, true);
255  break;
256  case 1:
257  // at the end OUT goes high
258  changeOut(timerIndex, true);
259  break;
260  case 2:
261  changeOut(timerIndex, false);
262  break;
263  case 3:
264  changeOut(timerIndex, !m_timer[timerIndex].out);
265  break;
266  }
267  } else {
268  // count running
269  switch (m_timer[timerIndex].mode) {
270  case 1:
271  // OUT is low while running
272  changeOut(timerIndex, false);
273  break;
274  case 2:
275  changeOut(timerIndex, true);
276  break;
277  case 4:
278  case 5:
279  // start low pulse
280  changeOut(timerIndex, false);
281  break;
282  }
283  }
284 
285  }
286 
287  }
288 
289  //printf("timer 0 count = %d\n", m_timer[0].count);
290 
291 }
292 
293 
294 } // namespace fabgl
Definition: canvas.cpp:36