aboutsummaryrefslogtreecommitdiffhomepage
path: root/source/Core/Src/QC3.cpp
blob: f5e2977f0f8537d902e3c54798bae7e345f45c0d (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
/*
 * QC3.c
 *
 *  Created on: 29 May 2020
 *      Author: Ralim
 */

// Quick charge 3.0 supporting functions
#include "QC3.h"
#include "BSP.h"
#include "cmsis_os.h"
#include "configuration.h"
#include "stdint.h"

enum QCState {
  NOT_STARTED = 0, // Have not checked
  QC_3        = 1,
  QC_2        = 2,
  NO_QC       = 3,
};

void QC_Seek9V() {
  QC_DNegZero_Six();
  QC_DPlusThree_Three();
}

void QC_Seek12V() {
  QC_DNegZero_Six();
  QC_DPlusZero_Six();
}

void QC_Seek20V() {
  QC_DNegThree_Three();
  QC_DPlusThree_Three();
}

void QC_SeekContMode() {
  QC_DNegThree_Three();
  QC_DPlusZero_Six();
}

void QC_SeekContPlus() {
  QC_SeekContMode();
  osDelay(30);
  QC_Seek20V();
  osDelay(10);
  QC_SeekContMode();
}

void QC_SeekContNeg() {
  QC_SeekContMode();
  osDelay(30);
  QC_Seek12V();
  osDelay(10);
  QC_SeekContMode();
}

QCState QCMode  = QCState::NOT_STARTED;
uint8_t QCTries = 0;
void    seekQC(int16_t Vx10, uint16_t divisor) {
  if (QCMode == QCState::NOT_STARTED) {
    startQC(divisor);
  }

  if (Vx10 < 40) { // Bail out if less than 4V
    return;
  }

  if (xTaskGetTickCount() < TICKS_SECOND) {
    return;
  }

  // Seek the QC to the Voltage given if this adapter supports continuous mode
  // try and step towards the wanted value

  // 1. Measure current voltage
  int16_t vStart     = getInputVoltageX10(divisor, 0);
  int     difference = Vx10 - vStart;

  // 2. calculate ideal steps (0.2V changes)

  int steps = difference / 2;
  if (QCMode == QCState::QC_3) {
    while (steps < 0) {
      QC_SeekContNeg();
      vTaskDelay(3 * TICKS_10MS);
      steps++;
    }
    while (steps > 0) {
      QC_SeekContPlus();
      vTaskDelay(3 * TICKS_10MS);
      steps--;
    }
    osDelay(100);
  }
#ifdef ENABLE_QC2
  // Re-measure
  /* Disabled due to nothing to test and code space of around 1k*/
  steps = vStart - getInputVoltageX10(divisor, 0);
  if (steps < 0) {
    steps = -steps;
  }
  if (steps > 4) {
    // No continuous mode, so QC2
    QCMode = QCState::QC_2;
    // Goto nearest
    if (Vx10 > 190) {
      // request 20V
      QC_Seek20V();
    } else if (Vx10 > 110) {
      // request 12V
      QC_Seek12V();
    } else {
      // request 9V
      QC_Seek9V();
    }
  }
#endif /* ENABLE_QC2 */
}

// Must be called after FreeRToS Starts
void startQC(uint16_t divisor) {
  // Pre check that the input could be >5V already, and if so, dont both
  // negotiating as someone is feeding in hv
  if (getInputVoltageX10(divisor, 0) > 80) {
    QCTries = 11;
    QCMode  = QCState::NO_QC;
    return;
  }
  if (QCTries > 10) {
    QCMode = QCState::NO_QC;
    return;
  }
  QCMode = QCState::NOT_STARTED;
  QC_Init_GPIO();

  // Tries to negotiate QC for 9V
  // This is a multiple step process.
  // 1. Set around 0.6V on D+ for 1.25 Seconds or so
  // 2. After this It should un-short D+->D- and instead add a 20k pulldown on
  // D-
  QC_DPlusZero_Six();

  // Delay 1.25 seconds
  uint8_t enteredQC = 0;
  for (uint16_t i = 0; i < 200 && enteredQC == 0; i++) {
    vTaskDelay(TICKS_10MS); // 10mS pause
    if (i > 130) {
      if (QC_DM_PulledDown()) {
        enteredQC = 1;
      }
      if (i == 140) {
        // For some marginal QC chargers, we try adding a pulldown
        QC_DM_PullDown();
      }
    }
  }

  QC_DM_No_PullDown();

  if (enteredQC) {
    // We have a QC capable charger
    QC_Seek9V();
    QC_Post_Probe_En();
    QC_Seek9V();
    // Wait for frontend ADC to stabilise
    QCMode = QCState::QC_2;
    for (uint8_t i = 0; i < 10; i++) {
      if (getInputVoltageX10(divisor, 0) > 80) {
        // yay we have at least QC2.0 or QC3.0
        QCMode = QCState::QC_3; // We have at least QC2, pray for 3
        return;
      }
      vTaskDelay(TICKS_100MS); // 100mS
    }
    QCMode = QCState::NOT_STARTED;
    QCTries++;
  } else {
    // no QC
    QCTries++;
    QCMode = QCState::NO_QC;
  }
}

bool hasQCNegotiated() { return QCMode == QCState::QC_3 || QCMode == QCState::QC_2; }