From: Alan R Evans Date: Mon, 25 Oct 2021 12:44:14 +0000 (+0000) Subject: 1st commit X-Git-Url: https://fbox.kageds.com/gitweb/richie-water-pump.git/commitdiff_plain/HEAD?ds=inline 1st commit --- dbac9bb48534b7e24a8ee3a27bbf49d5348e7823 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..422eebc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +if (TARGET tinyusb_device) + add_executable(pump + main.cpp + sevseg.cpp + ) + + # Pull in our pico_stdlib which pulls in commonly used features + target_link_libraries(pump pico_stdlib hardware_adc) + + # enable usb output, disable uart output + pico_enable_stdio_usb(pump 1) + pico_enable_stdio_uart(pump 0) + + # create map/bin/hex file etc. + pico_add_extra_outputs(pump) + + # add url via pico_set_program_url + example_auto_set_url(pump) +elseif(PICO_ON_DEVICE) + message(WARNING "not building pump because TinyUSB submodule is not initialized in the SDK") +endif() diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2456ec3 --- /dev/null +++ b/main.cpp @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + +#include +#include "pico/stdlib.h" +#include "hardware/gpio.h" +#include "hardware/adc.h" + +#include "sevseg.h" +SevSeg sevseg; //Instantiate a seven segment controller object + +#define PUMP_PIN 16 +#define BTN_PIN 11 + +volatile bool button_pressed = false; +volatile int time_left; +// Debounce control +volatile unsigned long time = to_ms_since_boot(get_absolute_time()); +const int delayTime = 300; // Delay for every push button may vary + +bool timer_cb(struct repeating_timer *t) { + time_left--; + return true; +} + +void button_cb(uint gpio, uint32_t events) { + if ((to_ms_since_boot(get_absolute_time())-time)>delayTime) { + time = to_ms_since_boot(get_absolute_time()); + printf("GPIO Interupt %d\n", gpio); + button_pressed = true; + } +} + +int main() { + const uint LED_PIN = PICO_DEFAULT_LED_PIN; + stdio_init_all(); + gpio_init(LED_PIN); + gpio_set_dir(LED_PIN, GPIO_OUT); + + gpio_init(PUMP_PIN); + gpio_set_dir(PUMP_PIN, GPIO_OUT); + gpio_put(PUMP_PIN, false); + + adc_init(); + // Make sure GPIO is high-impedance, no pullups etc + adc_gpio_init(26); + // Select ADC input 0 (GPIO26) + adc_select_input(0); + + // 12-bit conversion, assume max value == ADC_VREF == 3.3 V + const float conversion_factor = 60.0f / (1 << 12); + uint16_t result; + + byte numDigits = 2; + byte digitPins[] = {9, 21}; + byte segmentPins[] = {2, 3, 22, 28, 27, 7, 8, 9}; + bool resistorsOnSegments = true; // 'false' means resistors are on digit pins + byte hardwareConfig = COMMON_CATHODE; // See README.md for options + bool updateWithDelays = false; // Default. Recommended + bool leadingZeros = true; // Use 'true' if you'd like to keep the leading zeros + sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros); + + gpio_init(BTN_PIN); + gpio_pull_down(BTN_PIN); + gpio_set_irq_enabled_with_callback(BTN_PIN, GPIO_IRQ_EDGE_RISE, true, &button_cb); + + struct repeating_timer timer; + bool timer_running = false; + + while (true) { + gpio_put(LED_PIN, true); + if (button_pressed && !timer_running) { + add_repeating_timer_ms(-1000 * 60, timer_cb, NULL, &timer); + gpio_put(PUMP_PIN, true); + timer_running = true; + button_pressed= false; + } + else if (button_pressed && timer_running) { + cancel_repeating_timer(&timer); + gpio_put(PUMP_PIN, false); + timer_running = false; + button_pressed = false; + } + if (timer_running && time_left <= 0) { + cancel_repeating_timer(&timer); + timer_running = false; + gpio_put(PUMP_PIN, false); + } + if (!timer_running) { + result = adc_read(); + time_left = (int)(result * conversion_factor) + 1; + } + sevseg.setNumber(time_left, 0); + sevseg.refreshDisplay(); // Must run repeatedly + gpio_put(LED_PIN, false); + } +} diff --git a/sevseg.cpp b/sevseg.cpp new file mode 100644 index 0000000..e7b6bec --- /dev/null +++ b/sevseg.cpp @@ -0,0 +1,502 @@ +/* SevSeg Library + + Copyright 2017 Dean Reading + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + This library allows an Arduino to easily display numbers in decimal format on + a 7-segment display without a separate 7-segment display controller. + + Direct any questions or suggestions to deanreading@hotmail.com + See the included readme for instructions. + https://github.com/DeanIsMe/SevSeg + + CHANGELOG + Version 3.3.0 - February 2017 + Added the ability to keep leading zeros. This is now an extra + parameter in the begin() function. + + Version 3.2.0 - December 2016 + Updated to Arduino 1.5 Library Specification + New display function - no longer consumes processor time with delay() + Now supports hexadecimal number printing + The decimal point can now be omitted with a negative decPlaces + decPlaces is now optional in setNumber + Alphanumeric strings can be displayed (inaccurately) with setChars() + Removed #define RESISTORS_ON_SEGMENTS. Now a begin() input + Can now blank() the display + + Version 3.1 - September 2016 + Bug Fixes. No longer uses dynamic memory allocation. + Version 3.0 - November 2014 + Library re-design. A display with any number of digits can be used. + Floats are supported. Support for using transistors for switching. + Much more user friendly. No backwards compatibility. + Uploaded to GitHub to simplify any further development. + Version 2.3; Allows for brightness control. + Version 2.2; Allows 1, 2 or 3 digit displays to be used. + Version 2.1; Includes a bug fix. + Version 2.0; Now works for any digital pin arrangement. + Supports both common anode and common cathode displays. +*/ + +#include "sevseg.h" + +#include "pico/stdlib.h" + +#define BLANK_IDX 36 // Must match with 'digitCodeMap' +#define DASH_IDX 37 + +#define LOW false +#define HIGH true + +static const long powersOf10[] = { + 1, // 10^0 + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000 +}; // 10^9 + +static const long powersOf16[] = { + 0x1, // 16^0 + 0x10, + 0x100, + 0x1000, + 0x10000, + 0x100000, + 0x1000000, + 0x10000000 +}; // 16^7 + +// The codes below indicate which segments must be illuminated to display +// each number. +static const byte digitCodeMap[] = { + // GFEDCBA Segments 7-segment map: + 0x3f, // 0 + 0x06, // 1 + 0x5b, // 2 + 0x4f, // 3 + 0x66, // 4 + 0x6d, // 5 + 0x7d, // 6 + 0x07, // 7 + 0x7f, // 8 + 0x67 // 9 +}; + +// Constant pointers to constant data +const byte * const numeralCodes = digitCodeMap; + +// SevSeg Constructor +/******************************************************************************/ +SevSeg::SevSeg() +{ + // Initial value + ledOnTime = 2000; // Corresponds to a brightness of 100 + numDigits = 0; + prevUpdateIdx = 0; + prevUpdateTime = 0; + resOnSegments = 0; + updateWithDelays = 0; +} + + +// begin +/******************************************************************************/ +// Saves the input pin numbers to the class and sets up the pins to be used. +// If you use current-limiting resistors on your segment pins instead of the +// digit pins, then set resOnSegments as true. +// Set updateWithDelays to true if you want to use the 'pre-2017' update method +// That method occupies the processor with delay functions. +void SevSeg::begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[], + byte segmentPinsIn[], bool resOnSegmentsIn, + bool updateWithDelaysIn, bool leadingZerosIn) { + + resOnSegments = resOnSegmentsIn; + updateWithDelays = updateWithDelaysIn; + leadingZeros = leadingZerosIn; + + numDigits = numDigitsIn; + //Limit the max number of digits to prevent overflowing + if (numDigits > MAXNUMDIGITS) numDigits = MAXNUMDIGITS; + + switch (hardwareConfig) { + + case 0: // Common cathode + digitOn = LOW; + segmentOn = HIGH; + break; + + case 1: // Common anode + digitOn = HIGH; + segmentOn = LOW; + break; + + case 2: // With active-high, low-side switches (most commonly N-type FETs) + digitOn = HIGH; + segmentOn = HIGH; + break; + + case 3: // With active low, high side switches (most commonly P-type FETs) + digitOn = LOW; + segmentOn = LOW; + break; + } + + digitOff = !digitOn; + segmentOff = !segmentOn; + + // Save the input pin numbers to library variables + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + segmentPins[segmentNum] = segmentPinsIn[segmentNum]; + } + + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + digitPins[digitNum] = digitPinsIn[digitNum]; + } + + // Set the pins as outputs, and turn them off + for (byte digit = 0 ; digit < numDigits ; digit++) { + gpio_init(digitPins[digit]); + gpio_set_dir(digitPins[digit], GPIO_OUT); + gpio_put(digitPins[digit], segmentOff); + } + + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + gpio_init(segmentPins[segmentNum]); + gpio_set_dir(segmentPins[segmentNum], GPIO_OUT); + gpio_put(segmentPins[segmentNum], digitOff); + } + + setNewNum(0, 0); // Initialise the number displayed to 0 +} + + +// refreshDisplay +/******************************************************************************/ +// Turns on the segments specified in 'digitCodes[]' +// There are 4 versions of this function, with the choice depending on the +// location of the current-limiting resistors, and whether or not you wish to +// use 'update delays' (the standard method until 2017). +// For resistors on *digits* we will cycle through all 8 segments (7 + period), +// turning on the *digits* as appropriate for a given segment, before moving on +// to the next segment. +// For resistors on *segments* we will cycle through all __ # of digits, +// turning on the *segments* as appropriate for a given digit, before moving on +// to the next digit. +// If using update delays, refreshDisplay has a delay between each digit/segment +// as it cycles through. It exits with all LEDs off. +// If not using updateDelays, refreshDisplay exits with a single digit/segment +// on. It will move to the next digit/segment after being called again (if +// enough time has passed). + +void SevSeg::refreshDisplay() { + + if (!updateWithDelays) { + + // Exit if it's not time for the next display change + if (time_us_32() - prevUpdateTime < ledOnTime) return; + prevUpdateTime = time_us_32(); + + if (!resOnSegments) { + /**********************************************/ + // RESISTORS ON DIGITS, UPDATE WITHOUT DELAYS + + + // Turn all lights off for the previous segment + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + gpio_put(digitPins[digitNum], digitOff); + } + gpio_put(segmentPins[prevUpdateIdx], segmentOff); + + prevUpdateIdx++; + if (prevUpdateIdx >= 8) prevUpdateIdx = 0; + + byte segmentNum = prevUpdateIdx; + + // Illuminate the required digits for the new segment + gpio_put(segmentPins[segmentNum], segmentOn); + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit + gpio_put(digitPins[digitNum], digitOn); + } + } + } + else { + /**********************************************/ + // RESISTORS ON SEGMENTS, UPDATE WITHOUT DELAYS + + + // Turn all lights off for the previous digit + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + gpio_put(segmentPins[segmentNum], segmentOff); + } + gpio_put(digitPins[prevUpdateIdx], digitOff); + + prevUpdateIdx++; + if (prevUpdateIdx >= numDigits) prevUpdateIdx = 0; + + byte digitNum = prevUpdateIdx; + + // Illuminate the required segments for the new digit + gpio_put(digitPins[digitNum], digitOn); + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit + gpio_put(segmentPins[segmentNum], segmentOn); + } + } + } + } + + else { + if (!resOnSegments) { + /**********************************************/ + // RESISTORS ON DIGITS, UPDATE WITH DELAYS + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + + // Illuminate the required digits for this segment + gpio_put(segmentPins[segmentNum], segmentOn); + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit + gpio_put(digitPins[digitNum], digitOn); + } + } + + //Wait with lights on (to increase brightness) + sleep_us(ledOnTime); + + //Turn all lights off + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + gpio_put(digitPins[digitNum], digitOff); + } + gpio_put(segmentPins[segmentNum], segmentOff); + } + } + else { + /**********************************************/ + // RESISTORS ON SEGMENTS, UPDATE WITH DELAYS + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + + // Illuminate the required segments for this digit + gpio_put(digitPins[digitNum], digitOn); + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit + gpio_put(segmentPins[segmentNum], segmentOn); + } + } + + //Wait with lights on (to increase brightness) + sleep_us(ledOnTime); + + //Turn all lights off + for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) { + gpio_put(segmentPins[segmentNum], segmentOff); + } + gpio_put(digitPins[digitNum], digitOff); + } + } + } +} + + +// setNumber +/******************************************************************************/ +// This function only receives the input and passes it to 'setNewNum'. +// It is overloaded for all number data types, so that floats can be handled +// correctly. + +void SevSeg::setNumber(long numToShow, char decPlaces, bool hex) //long +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(unsigned long numToShow, char decPlaces, bool hex) //unsigned long +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(int numToShow, char decPlaces, bool hex) //int +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(unsigned int numToShow, char decPlaces, bool hex) //unsigned int +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(char numToShow, char decPlaces, bool hex) //char +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(byte numToShow, char decPlaces, bool hex) //byte +{ + setNewNum(numToShow, decPlaces, hex); +} + +void SevSeg::setNumber(float numToShow, char decPlaces, bool hex) //float +{ + char decPlacesPos = decPlaces; + if (hex) { + numToShow = numToShow * powersOf16[decPlacesPos]; + } + else { + numToShow = numToShow * powersOf10[decPlacesPos]; + } + // Modify the number so that it is rounded to an integer correctly + numToShow += (numToShow >= 0) ? 0.5f : -0.5f; + setNewNum(numToShow, decPlaces, hex); +} + + +// setNewNum +/******************************************************************************/ +// Changes the number that will be displayed. + +void SevSeg::setNewNum(long numToShow, char decPlaces, bool hex) { + byte digits[numDigits]; + findDigits(numToShow, decPlaces, hex, digits); + setDigitCodes(digits, decPlaces); +} + + +// setSegments +/******************************************************************************/ +// Sets the 'digitCodes' that are required to display the desired segments. +// Using this function, one can display any arbitrary set of segments (like +// letters, symbols or animated cursors). See setDigitCodes() for common +// numeric examples. +// +// Bit-segment mapping: 0bHGFEDCBA +// Visual mapping: +// AAAA 0000 +// F B 5 1 +// F B 5 1 +// GGGG 6666 +// E C 4 2 +// E C 4 2 (Segment H is often called +// DDDD H 3333 7 DP, for Decimal Point) + +void SevSeg::setSegments(byte segs[]) +{ + for (byte digit = 0; digit < numDigits; digit++) { + digitCodes[digit] = segs[digit]; + } +} + +// setChars +/******************************************************************************/ +// Displays the string on the display, as best as possible. +// Only numeric characters are supported +void SevSeg::setChars(char str[]) +{ + for (byte digit = 0; digit < numDigits; digit++) { + digitCodes[digit] = 0; + } + + for (byte digitNum = 0; digitNum < numDigits; digitNum++) { + char ch = str[digitNum]; + if (ch == '\0') break; // NULL string terminator + if (ch >= '0' && ch <= '9') { // Numerical + digitCodes[digitNum] = numeralCodes[ch - '0']; + } + } +} + +// blank +/******************************************************************************/ +void SevSeg::blank(void) { + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + digitCodes[digitNum] = digitCodeMap[BLANK_IDX]; + } + refreshDisplay(); +} + +// findDigits +/******************************************************************************/ +// Decides what each digit will display. +// Enforces the upper and lower limits on the number to be displayed. + +void SevSeg::findDigits(long numToShow, char decPlaces, bool hex, byte digits[]) { + const long * powersOfBase = hex ? powersOf16 : powersOf10; + const long maxNum = powersOfBase[numDigits] - 1; + const long minNum = -(powersOfBase[numDigits - 1] - 1); + + // If the number is out of range, just display dashes + if (numToShow > maxNum || numToShow < minNum) { + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + digits[digitNum] = DASH_IDX; + } + } + else { + byte digitNum = 0; + + // Convert all number to positive values + if (numToShow < 0) { + digits[0] = DASH_IDX; + digitNum = 1; // Skip the first iteration + numToShow = -numToShow; + } + + // Find all digits for base's representation, starting with the most + // significant digit + for ( ; digitNum < numDigits ; digitNum++) { + long factor = powersOfBase[numDigits - 1 - digitNum]; + digits[digitNum] = numToShow / factor; + numToShow -= digits[digitNum] * factor; + } + + // Find unnnecessary leading zeros and set them to BLANK + if (decPlaces < 0) decPlaces = 0; + if (!leadingZeros) { + for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++) { + if (digits[digitNum] == 0) { + digits[digitNum] = BLANK_IDX; + } + // Exit once the first non-zero number is encountered + else if (digits[digitNum] <= 9) { + break; + } + } + } + + } +} + + +// setDigitCodes +/******************************************************************************/ +// Sets the 'digitCodes' that are required to display the input numbers + +void SevSeg::setDigitCodes(byte digits[], char decPlaces) { + + // Set the digitCode for each digit in the display + for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) { + digitCodes[digitNum] = digitCodeMap[digits[digitNum]]; + // Set the decimal place segment + if (decPlaces >= 0) { + if (digitNum == numDigits - 1 - decPlaces) { + digitCodes[digitNum] |= 0x80; + } + } + } +} + +/// END /// diff --git a/sevseg.h b/sevseg.h new file mode 100644 index 0000000..a956218 --- /dev/null +++ b/sevseg.h @@ -0,0 +1,86 @@ +/* SevSeg Library + + Copyright 2017 Dean Reading + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + This library allows an Arduino to easily display numbers in decimal format on + a 7-segment display without a separate 7-segment display controller. + + Direct any questions or suggestions to deanreading@hotmail.com + See the included readme for instructions. + */ + +#ifndef MAXNUMDIGITS +#define MAXNUMDIGITS 8 // Can be increased, but the max number is 2^31 +#endif + +#ifndef SevSeg_h +#define SevSeg_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +typedef unsigned char byte; +#endif + +// Use defines to link the hardware configurations to the correct numbers +#define COMMON_CATHODE 0 +#define COMMON_ANODE 1 +#define N_TRANSISTORS 2 +#define P_TRANSISTORS 3 +#define NP_COMMON_CATHODE 1 +#define NP_COMMON_ANODE 0 + + +class SevSeg +{ +public: + SevSeg(); + + void refreshDisplay(); + void begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[], + byte segmentPinsIn[], bool resOnSegmentsIn=0, + bool updateWithDelaysIn=0, bool leadingZerosIn=0); + void setBrightness(int brightnessIn); // A number from 0..100 + + void setNumber(long numToShow, char decPlaces=-1, bool hex=0); + void setNumber(unsigned long numToShow, char decPlaces=-1, bool hex=0); + void setNumber(int numToShow, char decPlaces=-1, bool hex=0); + void setNumber(unsigned int numToShow, char decPlaces=-1, bool hex=0); + void setNumber(char numToShow, char decPlaces=-1, bool hex=0); + void setNumber(byte numToShow, char decPlaces=-1, bool hex=0); + void setNumber(float numToShow, char decPlaces=-1, bool hex=0); + + void setSegments(byte segs[]); + void setChars(char str[]); + void blank(void); + +private: + void setNewNum(long numToShow, char decPlaces, bool hex=0); + void findDigits(long numToShow, char decPlaces, bool hex, byte digits[]); + void setDigitCodes(byte nums[], char decPlaces); + + bool digitOn,digitOff,segmentOn,segmentOff; + bool resOnSegments, updateWithDelays, leadingZeros; + byte digitPins[MAXNUMDIGITS]; + byte segmentPins[8]; + byte numDigits; + byte prevUpdateIdx; + byte digitCodes[MAXNUMDIGITS]; + int ledOnTime; + unsigned long prevUpdateTime; +}; + +#endif //SevSeg_h +/// END ///