arduino_hardware.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include <Arduino.h>
  2. #include <avr/boot.h>
  3. #include <avr/eeprom.h>
  4. #include <avr/pgmspace.h>
  5. #include <firmware_runtime.h>
  6. #include <hardware_seam.h>
  7. #include "board_profile.h"
  8. static_assert(F_CPU == meCoffee::hardware::board::kClockFrequency, "Clock frequency does not match the board profile");
  9. static_assert(meCoffee::hardware::board::kAdcPrescaler == 128, "ADC register configuration requires prescaler 128");
  10. // Optional start-up padding that lines this image up with the original's start-up time in
  11. // differential runs. Zero in the default build.
  12. #ifndef MECOFFEE_CONSTRUCTION_ALIGNMENT_MICROSECONDS
  13. #define MECOFFEE_CONSTRUCTION_ALIGNMENT_MICROSECONDS 0
  14. #endif
  15. #ifndef MECOFFEE_SETUP_ALIGNMENT_MICROSECONDS
  16. #define MECOFFEE_SETUP_ALIGNMENT_MICROSECONDS 0
  17. #endif
  18. #include <mains_events.h>
  19. #include <pulse_tick.h>
  20. namespace meCoffee::hardware {
  21. void beginSerialLink(uint32_t baudRate) {
  22. #if MECOFFEE_SETUP_ALIGNMENT_MICROSECONDS > 0
  23. static bool isFirstCall = true;
  24. if (isFirstCall) {
  25. isFirstCall = false;
  26. delayMicroseconds(MECOFFEE_SETUP_ALIGNMENT_MICROSECONDS);
  27. }
  28. #endif
  29. Serial.begin(baudRate);
  30. }
  31. void writeSerialByte(uint8_t byteToTransmit) { Serial.write(byteToTransmit); }
  32. int readSerialByte() { return Serial.read(); }
  33. void waitForSerialTransmitCompletion() { Serial.flush(); }
  34. uint32_t readMillisecondCounter() { return millis(); }
  35. uint32_t readMicrosecondCounter() { return micros(); }
  36. void blockForMilliseconds(uint32_t durationInMilliseconds) { delay(durationInMilliseconds); }
  37. uint8_t readPersistentByte(uint16_t persistentAddress) {
  38. return eeprom_read_byte(reinterpret_cast<const uint8_t*>(persistentAddress));
  39. }
  40. void writePersistentByte(uint16_t persistentAddress, uint8_t byteToStore) {
  41. eeprom_write_byte(reinterpret_cast<uint8_t*>(persistentAddress), byteToStore);
  42. }
  43. uint8_t readProgramMemoryByte(const void* programMemoryAddress) { return pgm_read_byte(programMemoryAddress); }
  44. uint8_t readDeviceSignatureByte(uint8_t signatureByteIndex) {
  45. // The signature read must complete within a few cycles of the control write.
  46. const uint8_t savedStatusRegister = SREG;
  47. cli();
  48. const uint8_t signatureByte = boot_signature_byte_get(signatureByteIndex);
  49. SREG = savedStatusRegister;
  50. return signatureByte;
  51. }
  52. void configureLogicalPin(uint8_t logicalPin, PinConfiguration configuration) {
  53. const uint8_t physicalPin = board::physicalPin(logicalPin);
  54. switch (configuration) {
  55. case PinConfiguration::Input: pinMode(physicalPin, INPUT); break;
  56. case PinConfiguration::InputWithPullUp: pinMode(physicalPin, INPUT_PULLUP); break;
  57. case PinConfiguration::Output: pinMode(physicalPin, OUTPUT); break;
  58. }
  59. }
  60. void writeLogicalPin(uint8_t logicalPin, bool isHigh) { digitalWrite(board::physicalPin(logicalPin), isHigh ? HIGH : LOW); }
  61. bool readLogicalPin(uint8_t logicalPin) { return digitalRead(board::physicalPin(logicalPin)) != LOW; }
  62. void selectAnalogChannel(uint8_t channelNumber) { ADMUX = static_cast<uint8_t>(0xc0 | (board::analogChannel(channelNumber) & 0x0f)); }
  63. void startAnalogConversion() { ADCSRA |= _BV(ADSC); }
  64. bool isAnalogConversionRunning() { return (ADCSRA & _BV(ADSC)) != 0; }
  65. uint16_t readAnalogConversionResult() {
  66. const uint8_t lowByte = ADCL;
  67. const uint8_t highByte = ADCH;
  68. return static_cast<uint16_t>(lowByte | (highByte << 8));
  69. }
  70. void clearPulseOutputPins() {
  71. #if MECOFFEE_ARDUINO_16MHZ
  72. PORTD &= static_cast<uint8_t>(~(_BV(PD3) | _BV(PD4) | _BV(PD5)));
  73. #else
  74. PORTB &= static_cast<uint8_t>(~(_BV(PB1) | _BV(PB2)));
  75. PORTD &= static_cast<uint8_t>(~_BV(PD3));
  76. #endif
  77. }
  78. void writeRangeIndicatorPin(uint8_t rangeSelector, bool isHigh) {
  79. #if MECOFFEE_ARDUINO_16MHZ
  80. const uint8_t rangeMask = rangeSelector == 0 ? _BV(PC2) : _BV(PC3);
  81. if (isHigh) {
  82. PORTC |= rangeMask;
  83. } else {
  84. PORTC &= static_cast<uint8_t>(~rangeMask);
  85. }
  86. #else
  87. if (rangeSelector == 0) {
  88. if (isHigh) {
  89. PORTB |= _BV(PB7);
  90. } else {
  91. PORTB &= static_cast<uint8_t>(~_BV(PB7));
  92. }
  93. } else if (isHigh) {
  94. PORTD |= _BV(PD7);
  95. } else {
  96. PORTD &= static_cast<uint8_t>(~_BV(PD7));
  97. }
  98. #endif
  99. }
  100. void writeGenerationParityPin(bool isHigh) {
  101. #if MECOFFEE_ARDUINO_16MHZ
  102. if (isHigh) {
  103. PORTB |= _BV(PB3);
  104. } else {
  105. PORTB &= static_cast<uint8_t>(~_BV(PB3));
  106. }
  107. #else
  108. if (isHigh) {
  109. PORTC |= _BV(PC0);
  110. } else {
  111. PORTC &= static_cast<uint8_t>(~_BV(PC0));
  112. }
  113. #endif
  114. }
  115. void startPulseTimerAndMainsInterrupt() {
  116. // Mains edge: logical pin 2 (INT0), no pull-up, both edges.
  117. pinMode(2, INPUT);
  118. EICRA = static_cast<uint8_t>((EICRA & ~(_BV(ISC01) | _BV(ISC00))) | _BV(ISC00));
  119. EIMSK |= _BV(INT0);
  120. // Output pins that follow the sensor range flags and generation parity.
  121. #if MECOFFEE_ARDUINO_16MHZ
  122. DDRC |= _BV(PC2) | _BV(PC3);
  123. PORTC &= static_cast<uint8_t>(~(_BV(PC2) | _BV(PC3)));
  124. DDRB |= _BV(PB3);
  125. PORTB |= _BV(PB3);
  126. #else
  127. DDRB |= _BV(PB7);
  128. PORTB &= static_cast<uint8_t>(~_BV(PB7));
  129. DDRC |= _BV(PC0);
  130. PORTC |= _BV(PC0);
  131. #endif
  132. // Pulse timer: Timer 1, CTC, no prescaling, compare interrupt every 16.125 microseconds.
  133. TCCR1A = 0;
  134. TCCR1B = 0;
  135. TCNT1 = 0;
  136. OCR1A = board::kPulseTimerCompare;
  137. TCCR1B = _BV(WGM12) | _BV(CS10);
  138. TIMSK1 = _BV(OCIE1A);
  139. // Analog converter: internal reference, first temperature channel, prescaler 128.
  140. selectAnalogChannel(3);
  141. ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
  142. }
  143. void disableInterrupts() { cli(); }
  144. void enableInterrupts() { sei(); }
  145. uint8_t readOscillatorCalibrationRegister() { return OSCCAL; }
  146. void writeOscillatorCalibrationRegister(uint8_t calibrationValue) {
  147. if (!board::kUsesExternalClock) {
  148. OSCCAL = calibrationValue;
  149. }
  150. }
  151. } // namespace meCoffee::hardware
  152. ISR(TIMER1_COMPA_vect) {
  153. const meCoffee::PulseTickResult tickResult = meCoffee::advancePulseTick(&meCoffee::firmwareStateStorage);
  154. if ((tickResult.toggleMask & 0x01) != 0) {
  155. PIND = _BV(PD3);
  156. }
  157. if ((tickResult.toggleMask & 0x02) != 0) {
  158. #if MECOFFEE_ARDUINO_16MHZ
  159. PIND = _BV(PD4);
  160. #else
  161. PINB = _BV(PB2);
  162. #endif
  163. }
  164. if ((tickResult.toggleMask & 0x04) != 0) {
  165. #if MECOFFEE_ARDUINO_16MHZ
  166. PIND = _BV(PD5);
  167. #else
  168. PINB = _BV(PB1);
  169. #endif
  170. }
  171. if (tickResult.shouldStartConversion) {
  172. ADCSRA |= _BV(ADSC);
  173. }
  174. }
  175. ISR(INT0_vect) { meCoffee::processMainsEdge(&meCoffee::firmwareStateStorage); }
  176. namespace {
  177. // Mirrors the original's static constructors: settings are restored before the
  178. // Arduino runtime starts its millisecond timer.
  179. struct ConstructionTimeInitializer {
  180. ConstructionTimeInitializer() {
  181. meCoffee::initializeAtConstruction();
  182. #if MECOFFEE_CONSTRUCTION_ALIGNMENT_MICROSECONDS > 0
  183. delayMicroseconds(MECOFFEE_CONSTRUCTION_ALIGNMENT_MICROSECONDS);
  184. #endif
  185. }
  186. };
  187. const ConstructionTimeInitializer constructionTimeInitializer;
  188. } // namespace