From 2d1335a5e562ac15d7ebfe8a94d5b7b9f2f7383d Mon Sep 17 00:00:00 2001 From: MykolaKovalyk Date: Fri, 16 Aug 2024 15:15:20 +0300 Subject: [PATCH 1/3] Add first pipeline check --- .github/.gitignore | 1 - .github/actions/get_changed_files/action.yml | 13 ++ .github/workflows/Lab_01_CI.yml | 37 ------ .github/workflows/lab-validation.yml | 124 +++++++++++++++++++ 4 files changed, 137 insertions(+), 38 deletions(-) delete mode 100644 .github/.gitignore create mode 100644 .github/actions/get_changed_files/action.yml delete mode 100644 .github/workflows/Lab_01_CI.yml create mode 100644 .github/workflows/lab-validation.yml diff --git a/.github/.gitignore b/.github/.gitignore deleted file mode 100644 index 8b13789..0000000 --- a/.github/.gitignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.github/actions/get_changed_files/action.yml b/.github/actions/get_changed_files/action.yml new file mode 100644 index 0000000..ecf73ac --- /dev/null +++ b/.github/actions/get_changed_files/action.yml @@ -0,0 +1,13 @@ +name: "Get changed files list" +description: "Setup Node with caching for dependencies" +runs: + using: "composite" + steps: + - name: Get changed files list + id: get_changed_files + run: | + echo "Changed files:" + git diff --name-only ${{ github.event.pull_request.base.sha }} > changed_files.txt + cat changed_files.txt + echo "The list is saved to changed_files.txt" + shell: bash \ No newline at end of file diff --git a/.github/workflows/Lab_01_CI.yml b/.github/workflows/Lab_01_CI.yml deleted file mode 100644 index 985573b..0000000 --- a/.github/workflows/Lab_01_CI.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Cheking of compile Arduino sketch for AVR/AtMega and ESP8266 - -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] - -jobs: - test-matrix: - strategy: - matrix: - arduino-platform: - - "arduino:avr" - - "esp8266:esp8266" - include: - - arduino-platform: "arduino:avr" - fqbn: "arduino:avr:mega" - - arduino-platform: "esp8266:esp8266" - fqbn: "esp8266:esp8266:generic" - - runs-on: ubuntu-22.04 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Arduino CLI - uses: arduino/setup-arduino-cli@v2 - - - name: Install platform - run: | - arduino-cli core update-index - arduino-cli core install ${{ matrix.arduino-platform }} - - - name: Compile Sketch - run: arduino-cli compile --fqbn ${{ matrix.fqbn }} ./mc_labs/mc_lab_01/*.ino diff --git a/.github/workflows/lab-validation.yml b/.github/workflows/lab-validation.yml new file mode 100644 index 0000000..e74b6b8 --- /dev/null +++ b/.github/workflows/lab-validation.yml @@ -0,0 +1,124 @@ +name: Cheking of compile Arduino sketch for AVR/AtMega + +on: + pull_request: + branches: [main, master] + +env: + platform: "arduino:avr" + fqbn_master: "arduino:avr:mega" + COMMIT_COUNT: $(( ${{ github.event_name == 'pull_request' && github.event.pull_request.commits || 0 }} + 1 )) + +jobs: + handle_bad_branch_name: + runs-on: ubuntu-22.04 + if: (contains(github.head_ref, 'mc_lab_1') || contains(github.head_ref, 'mc_lab_2') || contains(github.head_ref, 'mc_lab_3') || contains(github.head_ref, 'mc_lab_4') || contains(github.head_ref, 'mc_lab_5') || contains(github.head_ref, 'mc_lab_6') || contains(github.head_ref, 'mc_lab_7')) == false + steps: + - name: Fail the build + run: | + echo "The branch name is not correct. It should contain 'mc_lab_' prefix" + exit 1 + build_labs_1_to_4: + runs-on: ubuntu-22.04 + if: contains(github.head_ref, 'mc_lab_1') || contains(github.head_ref, 'mc_lab_2') || contains(github.head_ref, 'mc_lab_3') || contains(github.head_ref, 'mc_lab_4') + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: ${{ env.COMMIT_COUNT }} + + - name: Get changed files list + uses: ./.github/actions/get_changed_files + + - name: Set up Arduino CLI + uses: arduino/setup-arduino-cli@v2 + + - name: Install platform + run: | + arduino-cli core update-index + arduino-cli core install ${{ env.platform }} + + - name: Compile Sketch + run: arduino-cli compile --fqbn ${{ env.fqbn_master }} $(grep -E '\.ino$' changed_files.txt | xargs) + build_lab_5: + runs-on: ubuntu-22.04 + if: contains(github.head_ref, 'mc_lab_5') + env: + fqbn_slave: "arduino:avr:nano" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: ${{ env.COMMIT_COUNT }} + + - name: Get changed files list + uses: ./.github/actions/get_changed_files + + - name: Get master folder + run: | + cat changed_files.txt | xargs dirname | grep 'master' | grep -m 1 -vE '/(.*master.*|.*slave.*)/' > master_project.txt + echo "Master project:" + cat master_project.txt + + - name: Get slave folders + run: | + cat changed_files.txt | xargs dirname | grep 'slave' | grep -vE '/(.*master.*|.*slave.*)/' > slave_projects.txt + echo "Slave projects:" + cat slave_projects.txt + + - name: Check if there is at least one master and one slave project + run: | + if [ ! -s master_project.txt ] || [ ! -s slave_projects.txt ]; then + echo "There is no master or slave project" + exit 1 + fi + + - name: Set up Arduino CLI + uses: arduino/setup-arduino-cli@v2 + + - name: Install platform + run: | + arduino-cli core update-index + arduino-cli core install ${{ env.platform }} + + - name: Compile master + run: while read master_folder; do arduino-cli compile --fqbn ${{ env.fqbn_master }} $master_folder/*.ino; done < master_project.txt + + - name: Compile slaves + run: while read slave_folder; do arduino-cli compile --fqbn ${{ env.fqbn_slave }} $slave_folder/*.ino; done < slave_projects.txt + build_lab_6: + runs-on: ubuntu-22.04 + if: contains(github.head_ref, 'mc_lab_6') + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: ${{ env.COMMIT_COUNT }} + + - name: It just passes + run: echo "It just passes. It's too complex" + build_lab_7: + runs-on: ubuntu-22.04 + if: contains(github.head_ref, 'mc_lab_7') + env: + register-bindings: "m2560def.inc" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: ${{ env.COMMIT_COUNT }} + + - name: Get changed files list + uses: ./.github/actions/get_changed_files + + - name: Setup AVRA Assembler + run: | + git clone https://github.com/Ro5bert/avra.git + cd avra + sudo make install + + - name: Preprocess sketch - append register bindings to the top of the file + run: printf ".include \"${{ env.register-bindings }}\"\n\n" | cat - $(grep -m 1 -E '\.(asm|S)$' changed_files.txt | xargs) > pipeline_main_assembly_source_file.asm + + - name: Compile Sketch + run: avra pipeline_main_assembly_source_file.asm From 5e51b794263c9766353a08cd76130c5421e51bad Mon Sep 17 00:00:00 2001 From: MykolaKovalyk Date: Sat, 17 Aug 2024 03:41:28 +0300 Subject: [PATCH 2/3] Add example code for lab 3 --- mc_labs/mc_lab_03/lab3-mko/keypad4x4.h | 184 +++++++++++++++++++++ mc_labs/mc_lab_03/lab3-mko/lab3-mko.ino | 203 ++++++++++++++++++++++++ mc_labs/mc_lab_03/lab3-mko/lab3.pdsprj | Bin 0 -> 25633 bytes 3 files changed, 387 insertions(+) create mode 100644 mc_labs/mc_lab_03/lab3-mko/keypad4x4.h create mode 100644 mc_labs/mc_lab_03/lab3-mko/lab3-mko.ino create mode 100644 mc_labs/mc_lab_03/lab3-mko/lab3.pdsprj diff --git a/mc_labs/mc_lab_03/lab3-mko/keypad4x4.h b/mc_labs/mc_lab_03/lab3-mko/keypad4x4.h new file mode 100644 index 0000000..c8595d3 --- /dev/null +++ b/mc_labs/mc_lab_03/lab3-mko/keypad4x4.h @@ -0,0 +1,184 @@ +#define DDR_KEYPAD DDRK +#define PORT_KEYPAD PORTK +#define PIN_KEYPAD PINK + + +class Keypad { +public: + bool pressed = false; + char d1, d2, d3, d4, dA, dB, dC, dD; + char columns[4]; + char rows[4]; + Keypad(char d1, char d2, char d3, char d4, char dA, char dB, char dC, char dD) + : columns({ d1, d2, d3, d4 }), + rows({ dA, dB, dC, dD }) { + this->d1 = d1; + this->d2 = d2; + this->d3 = d3; + this->d4 = d4; + this->dA = dA; + this->dB = dB; + this->dC = dC; + this->dD = dD; + } + + bool init() { + for (char row : rows) + pinMode(row, OUTPUT); + + + for (char column : columns) + pinMode(column, INPUT); + } + + bool isPressed() { + return pressed; + } + + char readKey() { + char* location = getPressedButtonLocation(); + + if (!pressed) + return '?'; + + delay(200); + return getKeyByLocation(location); + } + +private: + char* getPressedButtonLocation() { + char result[] = { 0, 0 }; + for (char rowToRead : rows) { + for (char row : rows) { + digitalWrite(row, HIGH); + asm("nop"); + } + char activeColumn; + for (char column : columns) { + pressed = digitalRead(column) == HIGH; + if (pressed) + activeColumn = column; + } + + + if (pressed) { + result[0] = rowToRead; + result[1] = activeColumn; + } + } + + return result; + } + + char getKeyByLocation(char location[2]) { + char key = '?'; + if (location[0] == dA && location[2] == d1) key = '7'; + if (location[0] == dB && location[2] == d1) key = '4'; + if (location[0] == dC && location[2] == d1) key = '1'; + if (location[0] == dD && location[2] == d1) key = 'F'; + if (location[0] == dA && location[2] == d2) key = '8'; + if (location[0] == dB && location[2] == d2) key = '5'; + if (location[0] == dC && location[2] == d2) key = '2'; + if (location[0] == dD && location[2] == d2) key = '0'; + if (location[0] == dD && location[2] == d3) key = '9'; + if (location[0] == dA && location[2] == d3) key = '6'; + if (location[0] == dB && location[2] == d3) key = '3'; + if (location[0] == dC && location[2] == d3) key = 'E'; + if (location[0] == dD && location[2] == d4) key = 'A'; + if (location[0] == dA && location[2] == d4) key = 'B'; + if (location[0] == dB && location[2] == d4) key = 'C'; + if (location[0] == dC && location[2] == d4) key = 'D'; + return key; + } +}; + +const int Key_1 = B10111110; +const int Key_2 = B10111101; +const int Key_3 = B10111011; +const int Key_A = B11100111; + +const int Key_4 = B11011110; +const int Key_5 = B11011101; +const int Key_6 = B11011011; +const int Key_B = B11010111; + +const int Key_7 = B11101110; +const int Key_8 = B11101101; +const int Key_9 = B11101011; +const int Key_C = B10110111; + +const int Key_F = B01111110; +const int Key_0 = B01111101; +const int Key_E = B01111011; +const int Key_D = B01110111; + +unsigned char freePinFromKeyPad = 1; +unsigned char keyFromKeyPad = 1; +char pressedButton = 0; +bool logicPressed = false; + +void initKeyPad() { + DDR_KEYPAD = 0x0F; + PORT_KEYPAD = 0xF0; +} + +char readKeyFromPad4x4() { + logicPressed = false; + return pressedButton; +} + +bool isButtonPressed() { + if (freePinFromKeyPad == 1) //перевірка чи була натиснута кнопка + { //якщо =1, тоді ще не натискалася + if (PIN_KEYPAD != 0xF0) { + delay(50); + freePinFromKeyPad = 0; + keyFromKeyPad = 1; + // Визначення натиснутої клавіші + // почергова подача 0V на рядки клавіатури A,B,C,D + PORT_KEYPAD = B11111110; // A-рядок + asm("nop"); + if (PORT_KEYPAD == PIN_KEYPAD) { + PORT_KEYPAD = B11111101; // B-рядок + asm("nop"); + if (PORT_KEYPAD == PIN_KEYPAD) { + PORT_KEYPAD = B11111011; // C-рядок + asm("nop"); + if (PORT_KEYPAD == PIN_KEYPAD) { + PORT_KEYPAD = B11110111; // D-рядок + asm("nop"); + if (PORT_KEYPAD == PIN_KEYPAD) + keyFromKeyPad = 0; // жодна клавіша не натиснута + } + } + } + if (keyFromKeyPad == 1) //визначення натиснутої клавіші + { + if (PIN_KEYPAD == Key_1) pressedButton = '1'; + else if (PIN_KEYPAD == Key_2) pressedButton = '2'; + else if (PIN_KEYPAD == Key_3) pressedButton = '3'; + else if (PIN_KEYPAD == Key_4) pressedButton = '4'; + else if (PIN_KEYPAD == Key_5) pressedButton = '5'; + else if (PIN_KEYPAD == Key_6) pressedButton = '6'; + else if (PIN_KEYPAD == Key_7) pressedButton = '7'; + else if (PIN_KEYPAD == Key_8) pressedButton = '8'; + else if (PIN_KEYPAD == Key_9) pressedButton = '9'; + else if (PIN_KEYPAD == Key_0) pressedButton = '0'; + else if (PIN_KEYPAD == Key_A) pressedButton = 'A'; + else if (PIN_KEYPAD == Key_B) pressedButton = 'B'; + else if (PIN_KEYPAD == Key_C) pressedButton = 'C'; + else if (PIN_KEYPAD == Key_D) pressedButton = 'D'; + else if (PIN_KEYPAD == Key_E) pressedButton = 'E'; + else if (PIN_KEYPAD == Key_F) pressedButton = 'F'; + logicPressed = true; + } + + PORT_KEYPAD = 0xF0; //відновлюємо порт + } + } else if (PIN_KEYPAD == 0xF0) //перевіряємо чи кнопка відпущена + { + delay(200); + freePinFromKeyPad = 1; + } + return logicPressed; +} diff --git a/mc_labs/mc_lab_03/lab3-mko/lab3-mko.ino b/mc_labs/mc_lab_03/lab3-mko/lab3-mko.ino new file mode 100644 index 0000000..f1c42c1 --- /dev/null +++ b/mc_labs/mc_lab_03/lab3-mko/lab3-mko.ino @@ -0,0 +1,203 @@ + + +// include the library code: +#include "keypad4x4.h" +#include + +#define RESET -1 +#define RETRIEVE -2 + +enum ClockMode { + Time, + Alarm, + AlarmSetup, + TimeSetup +}; + +const PROGMEM char sixty[60][2] = { + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", + "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" +}; + + +// initialize the library by associating any needed LCD interface pin +// with the arduino pin number it is connected to +const int padPins[] = { 62, 63, 64, 65, 66, 67, 98, 69 }; +const int rs = 22, rw = PJ6, en = 23; +const int ds[] = { 49, 48, 47, 46, 45, 44, 43, 42 }; +const int buzzerPin = 24; + +long time = 0; +long alarm = 0; +bool alarmActive = false; +bool previousAlarmSetFailed = false; +char mode = ClockMode::Time; +int inputData = 0; + +LiquidCrystal lcd(rs, en, ds[0], ds[1], ds[2], ds[3], ds[4], ds[5], ds[6], ds[7]); + +ISR(TIMER1_COMPA_vect) { + time++; +} + +String makeStr(String str) { + while(str.length() < 16) { + str += ' '; + } + return str; +} + +String secondsToTime(long time) { + String timeFormat = "00:00:00"; + + long hoursMinutesSeconds[] = { (time / (60 * 60)) % 24, (time / 60) % 60, time % 60 }; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + timeFormat[i * 3 + j] = (char)pgm_read_byte(&(sixty[hoursMinutesSeconds[i]][j])); + } + } + + return timeFormat; +} + +void handleButtonPress() { + if (isButtonPressed()) { + char value = readKeyFromPad4x4(); + switch (value) { + case 'A': + mode = ClockMode::TimeSetup; + getTime(RESET); + break; + case 'B': + mode = ClockMode::AlarmSetup; + getTime(RESET); + break; + case 'C': + mode = ClockMode::Alarm; + break; + case 'D': + if( mode == ClockMode::Alarm) + alarmActive = !alarmActive; + break; + case 'E': + if(mode == ClockMode::TimeSetup) { + mode = ClockMode::Time; + time = getTime(RETRIEVE); + } else if(mode == ClockMode::AlarmSetup) { + mode = ClockMode::Alarm; + alarm = getTime(RETRIEVE); + } + break; + case 'F': + mode = ClockMode::Time; + break; + default: + if(mode != ClockMode::TimeSetup && + mode != ClockMode::AlarmSetup) break; + + getTime(int(value - '0')); + break; + } + } +} + +void displayTime(long seconds) { + lcd.setCursor(0, 1); + + // print the number of seconds since reset: + lcd.print(makeStr(secondsToTime(seconds))); +} + + +long getTime(int inputNumber) { + static int currentValue[] = { 0, 0, 0, 0, 0, 0 }; + static int currentSymbol = 0; + + if(inputNumber == RESET) { + currentSymbol = 0; + for(int i = 0; i < 6; i++) + currentValue[i] = 0; + + displayTime(0); + return 0; + } + + int previousNumber = currentValue[currentSymbol]; + if(inputNumber != RETRIEVE) + currentValue[currentSymbol] = inputNumber; + + + long hours = (currentValue[0] * 10 + currentValue[1]); + long minutes = (currentValue[2] * 10 + currentValue[3]); + long seconds = (currentValue[4] * 10 + currentValue[5]); + + if(hours >= 24 || minutes >= 60 || seconds >= 60) { + currentValue[currentSymbol] = previousNumber; + return -1; + } + + long currentTime = (hours * 3600) + (minutes * 60) + seconds; + + if(inputNumber != RETRIEVE) { + displayTime(currentTime); + currentSymbol = (++currentSymbol) % 6; + } + + return currentTime; +} + +void checkAlarm() { + digitalWrite(buzzerPin, (alarmActive && ((time - alarm) % 2 == 0) && ((time - alarm) < 30) && ((time - alarm) > 0)) ? HIGH : LOW); +} + +void handleCurrentMode() { + lcd.setCursor(0, 0); + switch (mode) { + case ClockMode::TimeSetup: + lcd.print(makeStr("set time:")); + break; + case ClockMode::AlarmSetup: + lcd.print(makeStr("set alarm:")); + break; + case ClockMode::Alarm: + lcd.print(makeStr("alarm" + String(alarmActive ? " on" : " off") + ":")); + displayTime(alarm); + break; + case ClockMode::Time: + lcd.print(makeStr("time" + String(alarmActive ? ", alarm on" : "") + ":")); + displayTime(time); + break; + } +} + + +void setup() { + + initKeyPad(); + + noInterrupts(); + + // Таймер#1: Скид при співпадінні OCR1A (1sec) + TCCR1A = 0x00; + TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); //CTC mode & Prescaler @ 1024 + TIMSK1 = (1 << OCIE1A); // дозвіл на переривання по співпадінню + OCR1A = 0x3D08; // compare value = 1 sec (16MHz AVR) + + interrupts(); + + // set up the LCD's number of columns and rows: + lcd.begin(16, 2); + + pinMode(buzzerPin, OUTPUT); +} + +void loop() { + checkAlarm(); + handleButtonPress(); + handleCurrentMode(); +} \ No newline at end of file diff --git a/mc_labs/mc_lab_03/lab3-mko/lab3.pdsprj b/mc_labs/mc_lab_03/lab3-mko/lab3.pdsprj new file mode 100644 index 0000000000000000000000000000000000000000..c7b869f700df6437582aff0dbb3c656851f555f3 GIT binary patch literal 25633 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 E57xF)L;wH) literal 0 HcmV?d00001 From d8d20726ba5bb91c0fa76bfc5873c1487bb60f2c Mon Sep 17 00:00:00 2001 From: MykolaKovalyk Date: Sat, 17 Aug 2024 03:49:16 +0300 Subject: [PATCH 3/3] test --- .github/actions/avr_setup/action.yml | 18 ++++++++++++++++++ .github/workflows/lab-validation.yml | 14 ++------------ 2 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 .github/actions/avr_setup/action.yml diff --git a/.github/actions/avr_setup/action.yml b/.github/actions/avr_setup/action.yml new file mode 100644 index 0000000..3b92db0 --- /dev/null +++ b/.github/actions/avr_setup/action.yml @@ -0,0 +1,18 @@ +name: "Get changed files list" +description: "Setup Node with caching for dependencies" +runs: + using: "composite" + steps: + - name: Set up Arduino CLI + uses: arduino/setup-arduino-cli@v2 + + - name: Install platform + run: | + arduino-cli core update-index + arduino-cli core install ${{ env.platform }} + shell: bash + + - name: Install popular libs + run: | + arduino-cli lib install LiquidCrystal + shell: bash \ No newline at end of file diff --git a/.github/workflows/lab-validation.yml b/.github/workflows/lab-validation.yml index e74b6b8..059343b 100644 --- a/.github/workflows/lab-validation.yml +++ b/.github/workflows/lab-validation.yml @@ -31,12 +31,7 @@ jobs: uses: ./.github/actions/get_changed_files - name: Set up Arduino CLI - uses: arduino/setup-arduino-cli@v2 - - - name: Install platform - run: | - arduino-cli core update-index - arduino-cli core install ${{ env.platform }} + uses: ./.github/actions/avr_setup - name: Compile Sketch run: arduino-cli compile --fqbn ${{ env.fqbn_master }} $(grep -E '\.ino$' changed_files.txt | xargs) @@ -74,12 +69,7 @@ jobs: fi - name: Set up Arduino CLI - uses: arduino/setup-arduino-cli@v2 - - - name: Install platform - run: | - arduino-cli core update-index - arduino-cli core install ${{ env.platform }} + uses: ./.github/actions/avr_setup - name: Compile master run: while read master_folder; do arduino-cli compile --fqbn ${{ env.fqbn_master }} $master_folder/*.ino; done < master_project.txt