|
| 1 | +// lcd_test_1a |
| 2 | +// by André Derrick Balsa (AndrewBCN) |
| 3 | +// March 2022 |
| 4 | +// GPLV3 |
| 5 | +// Testing the LCD display with the STM32F401CCU6 |
| 6 | +// Also test SPI bus and I2C bus and various sensors |
| 7 | + |
| 8 | +#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION < 0x02020000) |
| 9 | + #error "Due to API changes, this sketch is compatible with STM32_CORE_VERSION >= 0x02020000 (2.2.0 or later)" |
| 10 | +#endif |
| 11 | + |
| 12 | +// Increase HardwareSerial (UART) TX and RX buffer sizes from default 64 characters to 256. |
| 13 | +// The main worry here is that we could miss some characters from the u-blox GPS module if |
| 14 | +// the processor is busy doing something else (e.g. updating the display, reading a sensor, etc) |
| 15 | +// specially since we increase the GPS baud rate from 9600 to 38400. |
| 16 | + |
| 17 | +#define SERIAL_TX_BUFFER_SIZE 256 // Warning: > 256 could cause problems, see comments in STM32 HardwareSerial library |
| 18 | +#define SERIAL_RX_BUFFER_SIZE 256 |
| 19 | + |
| 20 | +#define GPSDO_GEN_2kHz_PB5 // generate 2kHz square wave test signal on pin PB5 using Timer 3 |
| 21 | +#define GPSDO_INA219 // INA 219 current sensor |
| 22 | + |
| 23 | +#ifdef GPSDO_GEN_2kHz_PB5 |
| 24 | + #define Test2kHzOutputPin PB5 // digital output pin used to output a test 2kHz square wave |
| 25 | +#endif // GEN_2kHz_PB5 |
| 26 | + |
| 27 | +#ifdef GPSDO_INA219 |
| 28 | + #include <Adafruit_INA219.h> |
| 29 | + Adafruit_INA219 ina219; |
| 30 | +#endif // INA219 |
| 31 | + |
| 32 | +#define VctlPWMOutputPin PB9 // digital output pin used to output a PWM value, TIM4 ch4 |
| 33 | + // Two cascaded RC filters transform the PWM into an analog DC value |
| 34 | + |
| 35 | +#include <Wire.h> // Hardware I2C library on STM32 |
| 36 | + // Uses PB6 (SCL1) and PB7 (SDA1) on Black Pill for I2C1 |
| 37 | +#include <SPI.h> // Hardware SPI library on STM32 |
| 38 | + // Uses PA5, PA6, PA7 on Black Pill for SPI1 |
| 39 | +// AHT20 - I2C |
| 40 | +#include <Adafruit_AHTX0.h> // Adafruit AHTX0 library for AHT20 I2C temperature and humidity sensor |
| 41 | +Adafruit_AHTX0 aht20; // create object aht20 |
| 42 | + |
| 43 | +// BMP280 - I2C |
| 44 | +#include <Adafruit_BMP280.h> |
| 45 | +Adafruit_BMP280 bmp280; // hardware I2C |
| 46 | +Adafruit_Sensor *bmp_temp = bmp280.getTemperatureSensor(); |
| 47 | +Adafruit_Sensor *bmp_pressure = bmp280.getPressureSensor(); |
| 48 | + |
| 49 | +// TFT LCD ST7789 - SPI |
| 50 | +#include <Adafruit_GFX.h> // need this adapted for STM32F4xx/F411C: https://github.com/fpistm/Adafruit-GFX-Library/tree/Fix_pin_type |
| 51 | +#include <Adafruit_ST7789.h> |
| 52 | +//#include <Fonts/FreeSansBold18pt7b.h> |
| 53 | + |
| 54 | +#define TFT_DC PB12 // note this pin assigment conflicts with the original GPSDO schematic |
| 55 | +#define TFT_CS PB13 // in reality, not connected, CS not used on 1.3" TFT ST7789 display |
| 56 | +#define TFT_RST PB15 // also uses pins PA5, PA6, PA7 for MOSI MISO and SCLK |
| 57 | + |
| 58 | +Adafruit_ST7789 disp = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST); |
| 59 | + |
| 60 | +// LEDs |
| 61 | +// Blue onboard LED blinks to indicate ISR is working |
| 62 | +#define blueledpin PC13 // Blue onboard LED is on PC13 on STM32F411CEU6 Black Pill |
| 63 | +// Yellow extra LED is off, on or blinking to indicate some GPSDO status |
| 64 | +#define yellowledpin PB8 // Yellow LED on PB8 |
| 65 | +volatile int yellow_led_state = 2; // global variable 0=off 1=on 2=1Hz blink |
| 66 | + |
| 67 | +// Uptime data |
| 68 | +volatile uint8_t uphours = 0; |
| 69 | +volatile uint8_t upminutes = 0; |
| 70 | +volatile uint8_t upseconds = 0; |
| 71 | +volatile uint16_t updays = 0; |
| 72 | +volatile bool halfsecond = false; |
| 73 | +char uptimestr[9] = "00:00:00"; // uptime string |
| 74 | +char updaysstr[5] = "000d"; // updays string |
| 75 | + |
| 76 | + |
| 77 | +// Interrupt Service Routine for the 2Hz timer |
| 78 | +void Timer_ISR_2Hz(void) { // WARNING! Do not attempt I2C communication inside the ISR |
| 79 | + |
| 80 | + // Toggle pin. 2hz toogle --> 1Hz pulse, perfect 50% duty cycle |
| 81 | + digitalWrite(blueledpin, !digitalRead(blueledpin)); |
| 82 | + |
| 83 | + halfsecond = !halfsecond; // true @ 1Hz |
| 84 | + |
| 85 | + switch (yellow_led_state) |
| 86 | + { |
| 87 | + case 0: |
| 88 | + // turn off led |
| 89 | + digitalWrite(yellowledpin, LOW); |
| 90 | + break; |
| 91 | + case 1: |
| 92 | + // turn on led |
| 93 | + digitalWrite(yellowledpin, HIGH); |
| 94 | + break; |
| 95 | + case 2: |
| 96 | + // blink led |
| 97 | + digitalWrite(yellowledpin, !digitalRead(yellowledpin)); |
| 98 | + break; |
| 99 | + default: |
| 100 | + // default is to turn off led |
| 101 | + digitalWrite(yellowledpin, LOW); |
| 102 | + break; |
| 103 | + } |
| 104 | + |
| 105 | + // Uptime clock - in days, hours, minutes, seconds |
| 106 | + if (halfsecond) |
| 107 | + { |
| 108 | + if (++upseconds > 59) |
| 109 | + { |
| 110 | + upseconds = 0; |
| 111 | + if (++upminutes > 59) |
| 112 | + { |
| 113 | + upminutes = 0; |
| 114 | + if (++uphours > 23) |
| 115 | + { |
| 116 | + uphours = 0; |
| 117 | + ++updays; |
| 118 | + } |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | +} // end of 2Hz ISR |
| 123 | + |
| 124 | +void uptimetostrings() { |
| 125 | + // translate uptime variables to strings |
| 126 | + uptimestr[0] = '0' + uphours / 10; |
| 127 | + uptimestr[1] = '0' + uphours % 10; |
| 128 | + uptimestr[3] = '0' + upminutes / 10; |
| 129 | + uptimestr[4] = '0' + upminutes % 10; |
| 130 | + uptimestr[6] = '0' + upseconds / 10; |
| 131 | + uptimestr[7] = '0' + upseconds % 10; |
| 132 | + |
| 133 | + if (updays > 99) { // 100 days or more |
| 134 | + updaysstr[0] = '0' + updays / 100; |
| 135 | + updaysstr[1] = '0' + (updays % 100) / 10; |
| 136 | + updaysstr[2] = '0' + (updays % 100) % 10; |
| 137 | + } |
| 138 | + else { // less than 100 days |
| 139 | + updaysstr[0] = '0'; |
| 140 | + updaysstr[1] = '0' + updays / 10; |
| 141 | + updaysstr[2] = '0' + updays % 10; |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +void setup() { |
| 146 | + // Wait 1 second for things to stabilize |
| 147 | + delay(1000); |
| 148 | + |
| 149 | + // setup USB serial |
| 150 | + Serial.begin(9600); |
| 151 | + Serial.println(F("LCD display test")); |
| 152 | + |
| 153 | + // configure blueledpin in output mode |
| 154 | + pinMode(blueledpin, OUTPUT); |
| 155 | + |
| 156 | + // configure yellow_led_pin in output mode |
| 157 | + pinMode(yellowledpin, OUTPUT); |
| 158 | + |
| 159 | + // setup 2kHz test signal on PB5 if configured, uses Timer 3 |
| 160 | + #ifdef GPSDO_GEN_2kHz_PB5 // note this uses Timer 3 Channel 2 |
| 161 | + analogWrite(Test2kHzOutputPin, 127); // configures PB5 as PWM output pin at default frequency and resolution |
| 162 | + analogWriteFrequency(2000); // default PWM frequency is 1kHz, change it to 2kHz |
| 163 | + analogWriteResolution(16); // default PWM resolution is 8 bits, change it to 16 bits |
| 164 | + analogWrite(Test2kHzOutputPin, 32767); // 32767 for 16 bits -> 50% duty cycle so a square wave |
| 165 | + #endif // GEN_2kHz_PB5 |
| 166 | + |
| 167 | + // setup Vctl PWM DAC, output approximately 1.65V for testing purposes |
| 168 | + // we generate a 2kHz square wave on PB9 PWM pin, using Timer 4 channel 4 |
| 169 | + // PB9 is Timer 4 Channel 4 from Arduino_Core_STM32/variants/STM32F4xx/F411C(C-E)(U-Y)/PeripheralPins_BLACKPILL_F411CE.c |
| 170 | + analogWrite(VctlPWMOutputPin, 127); // configures PB9 as PWM output pin at default frequency and resolution |
| 171 | + analogWriteFrequency(2000); // default PWM frequency is 1kHz, change it to 2kHz |
| 172 | + analogWriteResolution(16); // set PWM resolution to 16 bits (the maximum for the STM32F411CEU6) |
| 173 | + analogWrite(VctlPWMOutputPin, 32767); // 32767 for 16 bits -> 50% duty cycle so a square wave |
| 174 | + |
| 175 | + // setup 2Hz timer and interrupt, uses Timer 9 |
| 176 | + HardwareTimer *tim2Hz = new HardwareTimer(TIM9); |
| 177 | + tim2Hz->setOverflow(2, HERTZ_FORMAT); // 2 Hz |
| 178 | + tim2Hz->attachInterrupt(Timer_ISR_2Hz); |
| 179 | + tim2Hz->resume(); |
| 180 | + |
| 181 | + // setup sensors and LCD display |
| 182 | + // AHT20, BMP280, INA219, ST7789 240x240 TFT LCD |
| 183 | + |
| 184 | + Serial.println(F("Testing for presence of AHT20 Sensor on I2C bus")); |
| 185 | + if (!aht20.begin()) { |
| 186 | + Serial.println(F("Could not find AHT20 sensor, check wiring")); |
| 187 | + while (1) delay(10); |
| 188 | + } |
| 189 | + else Serial.println(F("AHT20 sensor found!")); |
| 190 | + |
| 191 | + Serial.println(F("Testing for presence of BMP280 Sensor on I2C bus")); |
| 192 | + if (!bmp280.begin(0x76,0x58)) { |
| 193 | + Serial.println(F("Could not find BMP280 sensor, check wiring")); |
| 194 | + while (1) delay(10); |
| 195 | + } |
| 196 | + else Serial.println(F("BMP280 sensor found!")); |
| 197 | + |
| 198 | + // Default settings from datasheet |
| 199 | + bmp280.setSampling(Adafruit_BMP280::MODE_NORMAL, // Operating Mode |
| 200 | + Adafruit_BMP280::SAMPLING_X2, // Temp. oversampling |
| 201 | + Adafruit_BMP280::SAMPLING_X16, // Pressure oversampling |
| 202 | + Adafruit_BMP280::FILTER_X16, // Filtering |
| 203 | + Adafruit_BMP280::STANDBY_MS_500); // Standby time |
| 204 | + |
| 205 | + // Initialize the INA219. |
| 206 | + // By default the initialization will use the largest range (32V, 2A). However |
| 207 | + // you can call a setCalibration function to change this range (see comments). |
| 208 | + if (! ina219.begin()) { |
| 209 | + Serial.println(F("Could not find INA219 sensor, check wiring")); |
| 210 | + while (1) { delay(10); } |
| 211 | + } |
| 212 | + else Serial.println(F("INA219 sensor found!")); |
| 213 | + // To use a slightly lower 32V, 1A range (higher precision on amps): |
| 214 | + //ina219.setCalibration_32V_1A(); |
| 215 | + // Or to use a lower 16V, 400mA range (higher precision on volts and amps): |
| 216 | + //ina219.setCalibration_16V_400mA(); |
| 217 | + ina219.setCalibration_32V_1A(); |
| 218 | + |
| 219 | + // Setup 240x240 LCD SPI ST7789 display |
| 220 | + disp.init(240, 240, SPI_MODE3); // 1.3" 240x240 TFT LCD |
| 221 | + delay(500); |
| 222 | + disp.fillScreen(ST77XX_BLACK); |
| 223 | + disp.setTextColor(ST77XX_YELLOW, ST77XX_BLACK); // |
| 224 | + disp.setRotation(2); // 0..3 max, here we use 180° = landscape |
| 225 | + disp.setFont(); |
| 226 | + disp.setTextSize(3); |
| 227 | + disp.setCursor(0, 30); |
| 228 | + disp.print(F("Testing...")); |
| 229 | + disp.setTextSize(2); |
| 230 | + disp.setCursor(0, 60); |
| 231 | + disp.setTextColor(ST77XX_GREEN, ST77XX_BLACK); |
| 232 | + disp.print(F(" Smaller text - ")); |
| 233 | + disp.setTextColor(ST77XX_WHITE, ST77XX_BLACK); |
| 234 | + disp.print(F("123")); |
| 235 | + disp.setCursor(0, 80); |
| 236 | + disp.setTextColor(ST77XX_RED, ST77XX_BLACK); |
| 237 | + disp.print(F("Different colors.")); |
| 238 | + disp.setTextSize(3); |
| 239 | + disp.setTextColor(ST77XX_CYAN, ST77XX_BLACK); |
| 240 | + disp.setCursor(0, 120); |
| 241 | + disp.print(F("STM32 GPSDO")); |
| 242 | + disp.setTextSize(2); |
| 243 | + disp.setTextColor(ST77XX_MAGENTA, ST77XX_BLACK); |
| 244 | + disp.setCursor(0, 150); |
| 245 | + disp.print(F(" Version v0.99z")); |
| 246 | + disp.setTextColor(ST77XX_BLUE, ST77XX_BLACK); |
| 247 | + disp.setCursor(0, 180); |
| 248 | + disp.print(F(" ... not really!")); |
| 249 | +} // setup done |
| 250 | + |
| 251 | +void loop() { |
| 252 | + // print something once per second to USB serial (Arduino monitor) |
| 253 | + |
| 254 | + uptimetostrings(); // get updaysstr and uptimestr |
| 255 | + Serial.print(F("Uptime: ")); |
| 256 | + Serial.print(updaysstr); |
| 257 | + Serial.print(F(" ")); |
| 258 | + Serial.println(uptimestr); |
| 259 | + |
| 260 | + Serial.println(); |
| 261 | + |
| 262 | + sensors_event_t temp_event, pressure_event; |
| 263 | + bmp_temp->getEvent(&temp_event); |
| 264 | + bmp_pressure->getEvent(&pressure_event); |
| 265 | + |
| 266 | + Serial.println(F("BMP280 Sensor Readings")); |
| 267 | + Serial.print(F("Temperature = ")); |
| 268 | + Serial.print(temp_event.temperature); |
| 269 | + Serial.println(F(" *C")); |
| 270 | + |
| 271 | + Serial.print(F("Pressure = ")); |
| 272 | + Serial.print(pressure_event.pressure); |
| 273 | + Serial.println(F(" hPa")); |
| 274 | + |
| 275 | + Serial.println(); |
| 276 | + |
| 277 | + Serial.println(F("AHT20 Sensor Readings")); |
| 278 | + sensors_event_t humidity, temp; |
| 279 | + aht20.getEvent(&humidity, &temp); // populate temp and humidity objects with fresh data |
| 280 | + Serial.print(F("Temperature = ")); |
| 281 | + Serial.print(temp.temperature); |
| 282 | + Serial.println(F(" *C")); |
| 283 | + |
| 284 | + Serial.print(F("Humidity = ")); |
| 285 | + Serial.print(humidity.relative_humidity); |
| 286 | + Serial.println(F("% rH")); |
| 287 | + |
| 288 | + Serial.println(); |
| 289 | + |
| 290 | + // Read INA 219 current voltage sensor |
| 291 | + float shuntvoltage = 0; |
| 292 | + float busvoltage = 0; |
| 293 | + float current_mA = 0; |
| 294 | + float loadvoltage = 0; |
| 295 | + float power_mW = 0; |
| 296 | + |
| 297 | + shuntvoltage = ina219.getShuntVoltage_mV(); |
| 298 | + busvoltage = ina219.getBusVoltage_V(); |
| 299 | + current_mA = ina219.getCurrent_mA(); |
| 300 | + power_mW = ina219.getPower_mW(); |
| 301 | + loadvoltage = busvoltage + (shuntvoltage / 1000); |
| 302 | + |
| 303 | + Serial.print("Bus Voltage: "); Serial.print(busvoltage); Serial.println(" V"); |
| 304 | + Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV"); |
| 305 | + Serial.print("Load Voltage: "); Serial.print(loadvoltage); Serial.println(" V"); |
| 306 | + Serial.print("Current: "); Serial.print(current_mA); Serial.println(" mA"); |
| 307 | + Serial.print("Power: "); Serial.print(power_mW); Serial.println(" mW"); |
| 308 | + |
| 309 | + Serial.println(""); |
| 310 | + |
| 311 | + delay(2000); |
| 312 | +} |
0 commit comments