1941 lines
67 KiB
HTML
1941 lines
67 KiB
HTML
<!--
|
||
|
||
// ----------------------------------------------- //
|
||
// (Capture-)Compare-Simulation von Tim van den Boom //
|
||
// ~~~~ //
|
||
// Studienarbeit TEL23AEO 2025 im 5. Semester //
|
||
// Stand: 25.12.2ß25 //
|
||
// ----------------------------------------------- //
|
||
|
||
|
||
|
||
In diesem Code kommt generative KI zum Einsatz –
|
||
der Abschnitt, der dies betrifft, ist entsprechend markiert.
|
||
|
||
-->
|
||
|
||
<html>
|
||
<head>
|
||
<title>Demonstrator zur CCU</title>
|
||
</head>
|
||
<body onload="defaults();">
|
||
<div id="demo-wrapper">
|
||
<canvas id="main-canvas"></canvas>
|
||
</div>
|
||
<div id="controls">
|
||
<div class="control-tab-wrapper">
|
||
<div class="tab-list" id="tab-list-5">
|
||
<div onclick="changeTab(5,0)" id="indicator-5-0" class="active">TAxCTL</div>
|
||
<div onclick="changeTab(5,1)" id="indicator-5-1">Zoom</div>
|
||
<div onclick="changeTab(5,2)" id="indicator-5-2">Startwert</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-5-0" style="display: block;">
|
||
<input type="text" id="decimalValue-TAxCTL" value="MC_1 | ID_0 | TAIE | TASSEL_0" oninput="updateValueDisplay('TAxCTL','text',true)"/>
|
||
<span id="bin-hex-dec-display-TAxCTL-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCTL-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCTL-dec"></span>
|
||
<div class="info-box explain" id="explain-TAxCTL">
|
||
<img src="vendor/light-bulb-on.svg" />
|
||
<div id="explain-TAxCTL-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCTL" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCTL-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCTL-1" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCTL-1-text"></div>
|
||
</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-5-1" style="display: none;">
|
||
<input id="slider_zoom" type="range" min="25" max="300" value="250" oninput="updateValueDisplay('zoom','slider',true)"/>
|
||
<span id="zoom-display-perc" "></span>
|
||
<span id="zoom-display-rel"></span>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-5-2" style="display: none;">
|
||
<input id="startValue" type="checkbox" oninput="drawCanvas()"/>
|
||
<div class="info-box explain" >
|
||
<img src="vendor/light-bulb-on.svg" />
|
||
<div>
|
||
Wenn notwendig, können Sie hier einen durchgängig eingeschwungenen Zustand simulieren, sofern Ihr gewählter Output-Mode dies erfordert.<br>Die Ausgänge werden dann mit dem entsprechenden Startwert beschaltet.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="control-tab-wrapper">
|
||
<div class="tab-list" id="tab-list-6">
|
||
<div onclick="changeTab(6,0)" id="indicator-6-0" class="active">TAxCCTL0</div>
|
||
<div onclick="changeTab(6,1)" id="indicator-6-1">TAxCCTL1</div>
|
||
<div onclick="changeTab(6,2)" id="indicator-6-2">TAxCCTL2</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-6-0" style="display: block;">
|
||
<input type="text" id="decimalValue-TAxCCTL0" value="OUTMOD_1" oninput="updateValueDisplay('TAxCCTL0','text',true)"/>
|
||
<span id="bin-hex-dec-display-TAxCCTL0-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL0-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL0-dec"></span>
|
||
<div class="info-box explain" id="explain-TAxCCTL0">
|
||
<img src="vendor/light-bulb-on.svg" />
|
||
<div id="explain-TAxCCTL0-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL0" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL0-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL0-1" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL0-1-text"></div>
|
||
</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-6-1" style="display: none;">
|
||
<input type="text" id="decimalValue-TAxCCTL1" value="OUTMOD_0 | OUT" oninput="updateValueDisplay('TAxCCTL1','text',true)"/>
|
||
<span id="bin-hex-dec-display-TAxCCTL1-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL1-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL1-dec"></span>
|
||
<div class="info-box explain" id="explain-TAxCCTL1">
|
||
<img src="vendor/light-bulb-on.svg" />
|
||
<div id="explain-TAxCCTL1-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL1" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL1-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL1-1" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL1-1-text"></div>
|
||
</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-6-2" style="display: none;">
|
||
<input type="text" id="decimalValue-TAxCCTL2" value="OUTMOD1 | OUTMOD2" oninput="updateValueDisplay('TAxCCTL2','text',true)"/>
|
||
<span id="bin-hex-dec-display-TAxCCTL2-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL2-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCTL2-dec"></span>
|
||
<div class="info-box explain" id="explain-TAxCCTL2">
|
||
<img src="vendor/light-bulb-on.svg" />
|
||
<div id="explain-TAxCCTL2-text">
|
||
|
||
</div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL2" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL2-text"></div>
|
||
</div>
|
||
<div class="info-box warning" id="warning-TAxCCTL2-1" style="display: none;">
|
||
<img src="vendor/warning-triangle.svg" />
|
||
<div id="warning-TAxCCTL2-1-text"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="control-tab-wrapper" style="margin-bottom: 5px">
|
||
<div class="tab-list" id="tab-list-1">
|
||
<div onclick="changeTab(1,0)" id="indicator-1-0" class="active">TAxCCR0</div>
|
||
<div onclick="changeTab(1,1)" id="indicator-1-1">TAxCCR1</div>
|
||
<div onclick="changeTab(1,2)" id="indicator-1-2">TAxCCR2</div>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-1-0" style="display: block;">
|
||
<input type="text" id="decimalValue-TAxCCR0" value="0xBFFF" oninput="updateValueDisplay('TAxCCR0','text')"/>
|
||
<span id="bin-hex-dec-display-TAxCCR0-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR0-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR0-dec"></span>
|
||
<input id="slider_TAxCCR0" type="range" min="0" max="65535" oninput="updateValueDisplay('TAxCCR0','slider')"/>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-1-1" style="display: none;">
|
||
<input type="text" id="decimalValue-TAxCCR1" value="0x7FFF" oninput="updateValueDisplay('TAxCCR1','text')" onload="updateValueDisplay('TAxCCR1','text')"/>
|
||
<span id="bin-hex-dec-display-TAxCCR1-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR1-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR1-dec"></span>
|
||
<input id="slider_TAxCCR1" type="range" min="0" max="65535" oninput="updateValueDisplay('TAxCCR1','slider')"/>
|
||
</div>
|
||
<div class="bin-hex-dec-display" id="tab-1-2" style="display: none;">
|
||
<input type="text" id="decimalValue-TAxCCR2" value="0x3FFF" oninput="updateValueDisplay('TAxCCR2','text')"/>
|
||
<span id="bin-hex-dec-display-TAxCCR2-bin"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR2-hex"></span>
|
||
<span id="bin-hex-dec-display-TAxCCR2-dec"></span>
|
||
<input id="slider_TAxCCR2" type="range" min="0" max="65535" oninput="updateValueDisplay('TAxCCR2','slider')"/>
|
||
</div>
|
||
</div>
|
||
<i style="font-size: 10px; color: grey;">Studienarbeit von Tim van den Boom im Studiengang Elektrotechnik, 2025</i>
|
||
<input id="TAxCCR0" type="hidden"/>
|
||
<input id="TAxCCR1" type="hidden"/>
|
||
<input id="TAxCCR2" type="hidden"/>
|
||
<input id="TAxCCTL0" type="hidden"/>
|
||
<input id="TAxCCTL1" type="hidden"/>
|
||
<input id="TAxCCTL2" type="hidden"/>
|
||
<input id="timerMode" type="hidden" value="1"/>
|
||
<input id="outputMode0" type="hidden" value="4"/>
|
||
<input id="outputMode1" type="hidden" value="4"/>
|
||
<input id="outputMode2" type="hidden" value="4"/>
|
||
<input id="ramp" value="0" type="hidden"/>
|
||
<input id="zoom" value="250" type="hidden"/>
|
||
<input id="startValue" type="hidden"/>
|
||
<input id="TAxCTL" type="hidden"/>
|
||
<input id="TAIE" type="hidden"/>
|
||
<input id="TASSEL" type="hidden"/>
|
||
<input id="OUT0" type="hidden"/>
|
||
<input id="OUT1" type="hidden"/>
|
||
<input id="OUT2" type="hidden"/>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
|
||
<style>
|
||
|
||
@font-face {
|
||
font-family: 'Roboto Mono';
|
||
src: url('vendor/RobotoMono.ttf');
|
||
}
|
||
|
||
* {
|
||
font-size: 13px;
|
||
}
|
||
|
||
head, body {
|
||
padding: 0;
|
||
margin: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
font-family: Arial;
|
||
}
|
||
div#demo-wrapper, div#controls {
|
||
padding: 15px;
|
||
box-sizing: border-box;
|
||
display: inline-block;
|
||
float: left;
|
||
}
|
||
|
||
/*
|
||
Hier könnte man noch etwas anpassen.
|
||
|
||
Soll bspw. die Grafik etwas breiter dargestellt werden:
|
||
|
||
div#demo-wrapper {
|
||
width: 60%;
|
||
max-width: 90vh;
|
||
}
|
||
div#controls {
|
||
width: 40%;
|
||
}
|
||
|
||
*/
|
||
div#demo-wrapper {
|
||
width: 50%;
|
||
max-width: 90vh;
|
||
}
|
||
div#controls {
|
||
width: 50%;
|
||
max-width: 400px;
|
||
}
|
||
div#controls div.right, div#controls div.left {
|
||
width: 100%;
|
||
|
||
}
|
||
|
||
div.control-tab-wrapper {
|
||
padding: 0;
|
||
text-align: center;
|
||
margin-bottom: 15px;
|
||
}
|
||
div.control-tab-wrapper div.tab-list {
|
||
width: 100%;
|
||
height: 30px;
|
||
}
|
||
div.control-tab-wrapper div.tab-list div {
|
||
display: inline-block;
|
||
padding: 5px;
|
||
float: left;
|
||
height: 100%;
|
||
cursor: pointer;
|
||
background-color: #F5F5F5;
|
||
margin-right: 5px;
|
||
border-radius: 5px 5px 0 0 ;
|
||
}
|
||
|
||
|
||
div.control-tab-wrapper div.tab-list div:not(.active) {
|
||
height: 15px;
|
||
}
|
||
|
||
div.control-tab-wrapper div.tab-list div.active {
|
||
background-color: #E8E8E8;
|
||
}
|
||
|
||
|
||
div.bin-hex-dec-display {
|
||
padding: 6px;
|
||
text-align: center;
|
||
background-color: #E8E8E8;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
div.bin-hex-dec-display div.expandable-content {
|
||
display: none;
|
||
|
||
}
|
||
div.bin-hex-dec-display span {
|
||
display: inline-block;
|
||
width: calc(25% - 6px);
|
||
font-family: "Roboto Mono";
|
||
padding: 3px;
|
||
margin: 5px 5px 5px 0px;
|
||
box-sizing: border-box;
|
||
background-color: #F5F5F5;
|
||
border-radius: 5px;
|
||
}
|
||
div.bin-hex-dec-display span:first-of-type, div.bin-hex-dec-display span#zoom-display-rel {
|
||
width: calc(50% - 6px);
|
||
}
|
||
div.bin-hex-dec-display span:last-of-type {
|
||
margin-right: 0px;
|
||
}
|
||
div.bin-hex-dec-display input {
|
||
width: 100%;
|
||
}
|
||
div.bin-hex-dec-display select, div.bin-hex-dec-display input[type="text"] {
|
||
width: 100%;
|
||
background-color: #F5F5F5;
|
||
border: none;
|
||
border-radius: 5px;
|
||
padding: 5px;
|
||
border: 0px solid #BFBFBF;
|
||
}
|
||
div.bin-hex-dec-display input {
|
||
display: block;
|
||
float: none;
|
||
margin: 0;
|
||
}
|
||
|
||
|
||
br {
|
||
display: block;
|
||
margin-bottom: 1em;
|
||
content: "";
|
||
}
|
||
|
||
div.info-box {
|
||
width: 100%;
|
||
border-radius: 7px;
|
||
padding: 10px;
|
||
box-sizing: border-box;
|
||
margin: 0;
|
||
}
|
||
div.info-box.explain {
|
||
background-color: #c3d0e8;
|
||
}
|
||
div.info-box.warning {
|
||
background-color: #ea857a;
|
||
margin-top: 5px;
|
||
}
|
||
div.info-box>img {
|
||
width: 20px;
|
||
margin-bottom: auto;
|
||
float: left;
|
||
filter: opacity(.8);
|
||
}
|
||
div.info-box>div {
|
||
width: calc(100% - 50px);
|
||
display: block;
|
||
height: auto;
|
||
margin-left: 30px;
|
||
}
|
||
|
||
|
||
|
||
</style>
|
||
|
||
<script>
|
||
/*
|
||
// ---------------------------------------------------- FARBEN ----------------------------------------------------
|
||
*/
|
||
|
||
// Ausschnitt der Farbpalette von https://developer.gnome.org/hig/reference/palette.html
|
||
|
||
const colors = {
|
||
// Blue
|
||
"Blue 1": "#99c1f1",
|
||
"Blue 2": "#62a0ea",
|
||
"Blue 3": "#3584e4",
|
||
"Blue 4": "#1c71d8",
|
||
"Blue 5": "#1a5fb4",
|
||
|
||
// Dark Neutrals
|
||
"Dark 1": "#77767b",
|
||
"Dark 2": "#5e5c64",
|
||
"Dark 3": "#3d3846",
|
||
"Dark 4": "#241f31",
|
||
"Dark 5": "#000000"
|
||
};
|
||
|
||
/*
|
||
// ---------------------------------------------------- DEFAULT-WERTE ----------------------------------------------------
|
||
*/
|
||
|
||
function defaults() {
|
||
|
||
updateValueDisplay('TAxCTL', 'text', true);
|
||
updateValueDisplay('zoom', 'slider', true);
|
||
updateValueDisplay('TAxCCTL0', 'text', true);
|
||
updateValueDisplay('TAxCCTL1', 'text', true);
|
||
updateValueDisplay('TAxCCTL2', 'text', true);
|
||
|
||
updateValueDisplay('TAxCCR0', 'text');
|
||
updateValueDisplay('TAxCCR1', 'text');
|
||
updateValueDisplay('TAxCCR2', 'text');
|
||
}
|
||
|
||
/*
|
||
// ---------------------------------------------------- TAB-WECHSEL ----------------------------------------------------
|
||
*/
|
||
|
||
function changeTab(tabFamily, tab) {
|
||
// Alle Tabs und Indikatoren der angegebenen Familie finden
|
||
const tabs = document.querySelectorAll(`[id^="tab-${tabFamily}-"]`);
|
||
const indicators = document.querySelectorAll(`[id^="indicator-${tabFamily}-"]`);
|
||
|
||
// Alle Tabs ausblenden
|
||
tabs.forEach(el => el.style.display = "none");
|
||
|
||
// Gewählten Tab anzeigen
|
||
const activeTab = document.getElementById(`tab-${tabFamily}-${tab}`);
|
||
if (activeTab) activeTab.style.display = "block";
|
||
|
||
// Indikatoren zurücksetzen
|
||
indicators.forEach(el => el.classList.remove("active"));
|
||
|
||
// Aktiven Indikator aktivieren
|
||
const activeIndicator = document.getElementById(`indicator-${tabFamily}-${tab}`);
|
||
if (activeIndicator) activeIndicator.classList.add("active");
|
||
}
|
||
|
||
/*
|
||
// ---------------------------------------------------- CANVAS-INITIALISIERUNG ----------------------------------------------------
|
||
*/
|
||
|
||
const canvas = document.getElementById("main-canvas");
|
||
const ctx = canvas.getContext("2d");
|
||
|
||
// weil ich die Artefakte beim Verstellen der Fenstergröße nicht in dne Griff bekam, habe ich mir Hilfe von einer KI geholt! Dies hat sofort geklappt.
|
||
// dies ist der einzige mit KI generierte Code-Abschnitt!
|
||
const logicalWidth = 900;
|
||
const logicalHeight = 1020;
|
||
canvas.width = logicalWidth;
|
||
canvas.height = logicalHeight;
|
||
|
||
canvas.style.width = "100%";
|
||
canvas.style.height = "auto";
|
||
|
||
//Zeichnung entsprechend skalieren, falls nötig
|
||
// Damit Koordinaten immer mit der "logischen" Größe arbeiten
|
||
function resizeScale() {
|
||
const dpr = window.devicePixelRatio || 1;
|
||
const cssWidth = canvas.clientWidth;
|
||
const cssHeight = canvas.clientHeight;
|
||
|
||
canvas.width = cssWidth * dpr;
|
||
canvas.height = cssHeight * dpr;
|
||
|
||
const scaleX = cssWidth / logicalWidth;
|
||
const scaleY = cssHeight / logicalHeight;
|
||
|
||
// Kombinierte Skalierung: (DPR-Korrektur) * (Logische Skalierung)
|
||
ctx.setTransform(dpr * scaleX, 0, 0, dpr * scaleY, 0, 0);
|
||
}
|
||
|
||
//Ende KI-Generierter Code!
|
||
|
||
// Dies ist die Map für die nicht unterstützten Kontroll-Bits (vgl. C-Header-Datei; Auszug)
|
||
// diese stehen allesamt im Zusammenhang zum TimerA
|
||
// je nachdem, ob das genutzte Register in value[1] enthalten ist, erfolgt die Ausgabe "kenne ich nicht" oder "inkompatibel"
|
||
// es wäre unsinnig, wenn bei einem valide genutzten Register (z.B. TAxCTL) eine "Inkompatibilitätsmeldung" (z.B. durch TAxCCTL1) auftauchen würde (so ist etwa 0x0001 doppelt belegt)
|
||
const incompatibleBitValueMap = {
|
||
"TASSEL_1": [0x0100, ["TAxCTL"]],
|
||
"TASSEL_3": [0x0300, ["TAxCTL"]],
|
||
"TASSEL__ACLK": [0x0100, ["TAxCTL"]],
|
||
"TASSEL__INCLK": [0x0300, ["TAxCTL"]],
|
||
"CM_0": [0x0000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CM_1": [0x4000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CM_2": [0x8000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CM_3": [0xC000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CM1": [0x8000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CM0": [0x4000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS_0": [0x0000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS_1": [0x1000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS_2": [0x2000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS_3": [0x3000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS1": [0x0800, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIS0": [0x1000, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"SCS": [0x0800, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CAP": [0x0100, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCI": [0x0008, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"COV": [0x0002, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"CCIFG": [0x0001, ["TAxCCTL0", "TAxCCTL1", "TAxCCTL2"]],
|
||
"TACLR": [0x0004, ["TAxCTL"]],
|
||
"TAIFG": [0x0001, ["TAxCTL"]]
|
||
};
|
||
|
||
// Dies ist die Map für die unterstützten Kontroll-Bits (vgl. C-Header-Datei; Auszug)
|
||
const FLAGS = {
|
||
|
||
TASSEL_0: 0x0000,
|
||
TASSEL_2: 0x0200,
|
||
|
||
TASSEL__TACLK: 0x0000,
|
||
TASSEL__SMCLK: 0x0200,
|
||
|
||
ID__1: 0x0000,
|
||
ID__2: 0x0040,
|
||
ID__4: 0x0080,
|
||
ID__8: 0x00C0,
|
||
|
||
ID_0: 0x0000,
|
||
ID_1: 0x0040,
|
||
ID_2: 0x0080,
|
||
ID_3: 0x00C0,
|
||
|
||
MC_0: 0x0000,
|
||
MC_1: 0x0010,
|
||
MC_2: 0x0020,
|
||
MC_3: 0x0030,
|
||
|
||
MC__STOP: 0x0000,
|
||
MC__UP: 0x0010,
|
||
MC__CONTINUOUS: 0x0020,
|
||
MC__CONTINOUS: 0x0020,
|
||
MC__UPDOWN: 0x0030,
|
||
|
||
TAIE: 0x0002,
|
||
|
||
OUTMOD_0: 0x0000,
|
||
OUTMOD_1: 0x0020,
|
||
OUTMOD_2: 0x0040,
|
||
OUTMOD_3: 0x0060,
|
||
OUTMOD_4: 0x0080,
|
||
OUTMOD_5: 0x00A0,
|
||
OUTMOD_6: 0x00C0,
|
||
OUTMOD_7: 0x00E0,
|
||
|
||
OUTMOD2: 0x0080,
|
||
OUTMOD1: 0x0040,
|
||
OUTMOD0: 0x0020,
|
||
|
||
OUT: 0x0004
|
||
};
|
||
|
||
/*
|
||
// ---------------------------------------------------- WERTEANZEIGE ----------------------------------------------------
|
||
*/
|
||
|
||
// Funktion die initial bei Seitenaufruf und bei onchange="" aufgerufen wird
|
||
// unterscheidet in "type" (TAxCTL, ...), "source" (slider, text) und standalone (ohne korrespondierende(n) Slider/Texteingabe)
|
||
function updateValueDisplay(type, source, standalone) {
|
||
//die Quelle gibt an, woher der veränderte Wert stammt. Es darf nicht gleichzeitig vom Benutzer geschrieben und dann von der Software zurpckgeschrieben werden! Das würde unerwünschtes/buggendes Verhalten verursachen
|
||
|
||
// ist die Quelle ein Textfeld wird diese ausgelesen, sonst der Slider
|
||
// das Rückschreiben erfolgt später
|
||
if (source == "text") {
|
||
decimal = document.getElementById("decimalValue-" + type).value;
|
||
} else {
|
||
decimal = document.getElementById("slider_" + type).value;
|
||
}
|
||
|
||
// TAxCCTL0 und TAxCCTLn haben einen Sonderstatus, da hier auch Strings (also Kontrollbits) akzeptiert werden
|
||
// diese werden an dieser Stelle zunächst gesondert verarbeitet, bevor das Ergebnis weitergegeben wird
|
||
if (type == "TAxCCTL0" || type == "TAxCCTL1" || type == "TAxCCTL2" || type == "TAxCTL") {
|
||
// getrennt durch '+' (CCIE + OUTMOD_4 +...)
|
||
|
||
// sum = Summe der einzelnen Bit-Werte, errorParts = unbekannte Eingaben, incompatibleParts = in diesem Eingabefeld nicht unterstützte Eingaben
|
||
let sum = 0;
|
||
let errorParts = []; // unbekannte Konstanten
|
||
let incompatibleParts = []; // bekannte, aber nicht unterstützte
|
||
let messages = [];
|
||
|
||
// Auswerten der Flag-Contanten mit eval
|
||
function evalFlags(expr) {
|
||
try {
|
||
// Ausdruck mit Klassifizierung checken
|
||
const ids = expr.match(/\b[A-Za-z_][A-Za-z0-9_]*\b/g) || [];
|
||
|
||
for (const id of ids) {
|
||
|
||
if (FLAGS.hasOwnProperty(id)) {
|
||
// gültig & unterstützt
|
||
}
|
||
// wenn die Map der nicht untersüttzen Werte den aktuellen typ hält und die Flag definiert ist -> inkompatibel
|
||
else if (incompatibleBitValueMap.hasOwnProperty(id) && incompatibleBitValueMap[id][1].includes(type)) {
|
||
incompatibleParts.push(id);
|
||
} else {
|
||
// komplett unbekannt
|
||
errorParts.push(id);
|
||
}
|
||
}
|
||
|
||
// Wenn Fehler oder inkompatible Werte > KEIN EVAL
|
||
if (errorParts.length > 0 || incompatibleParts.length > 0) {
|
||
// setze alles 0
|
||
return 0;
|
||
}
|
||
|
||
// wenn alles korrekt durchlaufen ist, wird EVAL ausgeführt. Die Fehlerausgabe von Eval zeigt dann an, ob völlig unbekannte Flags verwendet wurdn
|
||
return (function() {
|
||
with(FLAGS) {
|
||
return eval(expr);
|
||
}
|
||
})();
|
||
|
||
} catch (err) {
|
||
messages.push(err.message + ". Es wird kein Ausganggsignal erstellt.");
|
||
// setze alles 0
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
sum = evalFlags(decimal);
|
||
|
||
// unbekannte Konstanten
|
||
if (errorParts.length > 0) {
|
||
messages.push(
|
||
`Die Anweisung(en) "${errorParts.join(" & ")}" verstehe ich nicht! Es wird kein Ausganggsignal erstellt.`
|
||
);
|
||
}
|
||
|
||
// inkompatible / nicht unterstützte Konstanten
|
||
if (incompatibleParts.length > 0) {
|
||
messages.push(
|
||
`Die Anweisung "${incompatibleParts.join(" & ")}" ist zwar eine existierende Einstellung, wird jedoch in dieser Simulation nicht unterstützt!`
|
||
);
|
||
}
|
||
|
||
// WICHTIG: an späterer Stelle wird noch einmal die Eingabe überprüft, jedoch auch die (Hexa)Dezimalen & Binären Eingaben!
|
||
// (es wurde erkennt, dass Sie versuchen xy zu verwenden...) -> das geschieht aber GETRENNT hiervon!
|
||
// damit die Meldung auch nicht zwei Mal auftaucht, wird der Wert erst gar nicht addiert (return;)
|
||
|
||
// Ausgabe oder Hinweisbox ausblenden
|
||
if (messages.length > 0) {
|
||
document.getElementById(`warning-${type}`).style.display = "block";
|
||
document.getElementById(`warning-${type}-text`).innerHTML = messages.join("<br>");
|
||
} else {
|
||
try {
|
||
document.getElementById(`warning-${type}`).style.display = "none";
|
||
} catch (error) {
|
||
console.warn("Zu diesem Wert existiert keine Hinweisbox.");
|
||
}
|
||
}
|
||
|
||
// dies ist das Destillat: das hexadezimale Pendant zur "String"-Eingabe
|
||
decimal = "0x" + sum.toString(16).toUpperCase().padStart(4, "0");
|
||
}
|
||
|
||
// hier geht es für die Restlichen Werte (also zoom, CCRn...) erst los!
|
||
|
||
// Credit: Heintz
|
||
// Stelle sicher, dass der Wert im Bereich von 0-255 liegt (unteres Byte)
|
||
const validValue = decimal & 0xFFFF; // 16-Bit Maske = 65535 dezimal
|
||
decimalValueDisplay = validValue.toString().padStart(5, '0'); // bis 65535 (5 Stellen)
|
||
hexValueDisplay = '0x' + validValue.toString(16).padStart(4, '0').toUpperCase();
|
||
binValueDisplay = '0b' + validValue.toString(2).padStart(16, '0');
|
||
// Ende Credit Heintz
|
||
|
||
// der Wert wird jeweils in drei Stellenwertsystemen simultan ausgegeben, abhängig vom veränderten Eingabefeld
|
||
if (type != 'ramp' && type !== 'zoom') {
|
||
document.getElementById("bin-hex-dec-display-" + type + "-hex").textContent = hexValueDisplay;
|
||
document.getElementById("bin-hex-dec-display-" + type + "-bin").textContent = binValueDisplay;
|
||
document.getElementById("bin-hex-dec-display-" + type + "-dec").textContent = decimalValueDisplay;
|
||
}
|
||
|
||
//der Slider-Value (also Select-Option, sliderValue) lässt keine führenden Nullen zu!
|
||
decimalValueDisplay = String(Number(decimalValueDisplay));
|
||
|
||
//schreibe den Wert ins Value
|
||
document.getElementById(type).value = decimalValueDisplay;
|
||
|
||
//schreibe den Wert ins entsprechende Feld
|
||
//ausnahme: Beim Body-Onload müssen beide Felder initial beschrieben werden
|
||
if (!standalone) {
|
||
if (source == "text") {
|
||
document.getElementById("slider_" + type).value = decimalValueDisplay;
|
||
} else {
|
||
if (source == "slider") {
|
||
document.getElementById("decimalValue-" + type).value = hexValueDisplay;
|
||
} else {
|
||
document.getElementById("slider_" + type).value = decimalValueDisplay;
|
||
document.getElementById("decimalValue-" + type).value = hexValueDisplay;
|
||
}
|
||
}
|
||
}
|
||
|
||
// zwei Kleine Funktionen um die Bits zu isolieren und deren Wert zu extrahieren
|
||
// genutzt wird Stellenwertverschiebung und Maskierung
|
||
function getBit(value, bitIndex) {
|
||
return (value >> bitIndex) & 1;
|
||
}
|
||
|
||
function getBits(value, from, to) {
|
||
const mask = ((1 << (to - from + 1)) - 1) << from;
|
||
return (value & mask) >> from;
|
||
}
|
||
|
||
// etwas unschön: Extraktion der relevanten Bit-Stellen
|
||
// diese werden dann in die versteckten HTML-Eingabefelder zurückgeschrieben, um später wieder drauf zuzugreifen
|
||
if (type == "TAxCTL") {
|
||
document.getElementById("timerMode").value = getBits(validValue, 4, 5);
|
||
document.getElementById("ramp").value = getBits(validValue, 6, 7);
|
||
document.getElementById("TAIE").checked = getBit(validValue, 1);
|
||
document.getElementById("TASSEL").value = getBits(validValue, 8, 9);
|
||
}
|
||
|
||
if (type == "TAxCCTL0") {
|
||
document.getElementById("outputMode0").value = getBits(validValue, 5, 7);
|
||
document.getElementById("OUT0").checked = getBit(validValue, 2);
|
||
}
|
||
if (type == "TAxCCTL1") {
|
||
document.getElementById("outputMode1").value = getBits(validValue, 5, 7);
|
||
document.getElementById("OUT1").checked = getBit(validValue, 2);
|
||
}
|
||
if (type == "TAxCCTL2") {
|
||
document.getElementById("outputMode2").value = getBits(validValue, 5, 7);
|
||
document.getElementById("OUT2").checked = getBit(validValue, 2);
|
||
}
|
||
|
||
// hier wird jetzt die gesamte Eingabe (also die (Hexa)Dezimalen & Binären Eingaben) auf Inkompatibilität geprüft
|
||
if (type == "TAxCCTL0" || type == "TAxCCTL1" || type == "TAxCCTL2" || type == "TAxCTL") {
|
||
|
||
// leere wieder die Liste inkompatibler Eingaben
|
||
let incompatible = [];
|
||
|
||
// wurde eine inkompatible Einstellung getroffen?
|
||
// part ist z.B. CCIS_3, type ist z.B. TAxCCTL1
|
||
// vgl: "CCIS_1": [0x1000, ["TAxCCTL0","TAxCCTL1","TAxCCTL2"]]
|
||
// anders als zuvor oben wird zudem sichergestellt, dass der Wert nicht =0 ist.
|
||
//Andernfalls würden dauerhaft die Meldungen von z.B. CM_0 oder CCIS_0 auftauchen!
|
||
for (const [name, value] of Object.entries(incompatibleBitValueMap)) {
|
||
if ((validValue & value[0]) === value[0] && value[0] !== 0 && value[1].includes(type)) {
|
||
incompatible.push(name);
|
||
}
|
||
}
|
||
|
||
let messages = [];
|
||
|
||
// Inkompatible Register
|
||
if (incompatible.length > 0) {
|
||
messages.push(
|
||
`Es wurde erkannt, dass Sie versuchen die Bits von "${incompatible.join(" & ")}" anzusprechen. Das ist zwar eine existierende Einstellung, wird jedoch in dieser Simulation nicht unterstützt! Bitte entfernen.`
|
||
);
|
||
}
|
||
|
||
// Ausgabe über die Hinweisbox
|
||
if (messages.length > 0) {
|
||
console.warn(`warning-${type}-1`);
|
||
document.getElementById(`warning-${type}-1`).style.display = "block";
|
||
document.getElementById(`warning-${type}-1-text`).innerHTML = messages.join("<br>");
|
||
} else {
|
||
try {
|
||
document.getElementById(`warning-${type}-1`).style.display = "none";
|
||
} catch (error) {
|
||
console.warn("Zu diesem Wert existiert keine Hinweisbox.");
|
||
}
|
||
}
|
||
}
|
||
|
||
//bei verändertem Wert -> Canvas neu zeichnen
|
||
drawCanvas();
|
||
}
|
||
|
||
// die Divider-Map definiert den Faktor, mit welchem die Anzeige skaliert wird (also Breite der Anzeige = Kehrwert der Zweierpotenz)
|
||
const dividerMap = {
|
||
"0": 1,
|
||
"1": 2,
|
||
"2": 4,
|
||
"3": 8
|
||
};
|
||
|
||
/*
|
||
// ---------------------------------------------------- BEGINN: ZEICHNEN ----------------------------------------------------
|
||
*/
|
||
|
||
function drawCanvas() {
|
||
// Eingabewert holen
|
||
const zoom = parseInt(document.getElementById("zoom").value);
|
||
const rampOriginal = dividerMap[document.getElementById("ramp").value];
|
||
const ramp = rampOriginal * (zoom / 100);
|
||
|
||
// Anzeige für die Zoomstufe
|
||
document.getElementById("zoom-display-perc").textContent = (zoom).toFixed(0) + "%";
|
||
document.getElementById("zoom-display-rel").textContent = "1:" + (zoom / 100).toFixed(2);
|
||
|
||
// Simulationsrelevante Werte holen
|
||
const timerMode = parseInt(document.getElementById("timerMode").value); // Hole Timer-Mode aus Select-Feld
|
||
const outputMode0 = parseInt(document.getElementById("outputMode0").value); // Hole Output-Mode aus Select-Feld
|
||
const outputMode1 = parseInt(document.getElementById("outputMode1").value); // Hole Output-Mode aus Select-Feld
|
||
const outputMode2 = parseInt(document.getElementById("outputMode2").value); // Hole Output-Mode aus Select-Feld
|
||
const TAIE = document.getElementById("TAIE").checked; // Hole Startwert aus Select-Feld
|
||
const TASSEL = parseInt(document.getElementById("TASSEL").value); // Hole Startwert aus Select-Feld
|
||
const OUT0 = document.getElementById("OUT0").checked;
|
||
const OUT1 = document.getElementById("OUT1").checked;
|
||
const OUT2 = document.getElementById("OUT2").checked;
|
||
let TAxCCR0_val = parseInt(document.getElementById("TAxCCR0").value);
|
||
let TAxCCR1_val = parseInt(document.getElementById("TAxCCR1").value);
|
||
let TAxCCR2_val = parseInt(document.getElementById("TAxCCR2").value);
|
||
const startValue = document.getElementById("startValue").checked;
|
||
console.log(startValue);
|
||
|
||
// die filterList ist eine übergreifende Einstellung und beschreibt, welche Schnittpunkte verarbeitet werden sollen
|
||
// die Schnittpunkte werden zwar alle gespichert, an späterer Stelle aber nicht ausgegeben!
|
||
const filterList = [];
|
||
// TAIE aktiviert TAIFG
|
||
if (TAIE) {
|
||
filterList.push("TAIFG");
|
||
}
|
||
// EQU 0-2 ist immer aktiv
|
||
filterList.push("TAxCCR0");
|
||
filterList.push("TAxCCR1");
|
||
filterList.push("TAxCCR2")
|
||
|
||
// hier geht's los mit dem Zeichnen des Canvas!
|
||
// Canvas löschen
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
||
// Haupt-Einstellungen
|
||
ctx.beginPath();
|
||
ctx.setLineDash([]);
|
||
ctx.font = "20px Roboto Mono";
|
||
ctx.textAlign = "right";
|
||
ctx.textBaseline = "bottom";
|
||
|
||
if (TAxCCR0_val <= TAxCCR1_val && timerMode !== 2) { // Fallback, wenn TAxCCR0 >= TAxCCR1 ist
|
||
//TAxCCR1_val = TAxCCR0_val - 1;
|
||
ctx.fillText("TAR wird TAxCCR1 niemals erreichen!", 700, 30); // Hinweisbox
|
||
}
|
||
if (TAxCCR0_val <= TAxCCR2_val && timerMode !== 2) { // Fallback, wenn TAxCCR0 >= TAxCCR1 ist
|
||
//TAxCCR2_val = TAxCCR1_val - 1;
|
||
ctx.fillText("TAR wird TAxCCR2 niemals erreichen!", 700, 50); // Hinweisbox
|
||
}
|
||
|
||
// Normierung des 16-Bit-Werts auf die 200px-Höhe des Wert-Zeit-Diagramms des Timer_A
|
||
const TAxCCR0_pos = ((65536 - TAxCCR0_val) / 65536) * 200;
|
||
const TAxCCR1_pos = ((65536 - TAxCCR1_val) / 65536) * 200;
|
||
const TAxCCR2_pos = ((65536 - TAxCCR2_val) / 65536) * 200;
|
||
|
||
graphBoxMarginLeft = 110; // Margin bis zu linkten Canvas-Begrenzung (Text und Achse orienitert sich hieran)
|
||
graphBoxMarginTop = 10; // Margin bis zu oberen Canvas-Begrenzung (Text und Achse orienitert sich hieran)
|
||
|
||
// Array, das die Schnittpunkte erfasst
|
||
graphIntersections = [];
|
||
|
||
// Das sind die Grundlinien des Koordinatensystems ("Box")
|
||
ctx.moveTo(graphBoxMarginLeft + 0, graphBoxMarginTop + 0);
|
||
ctx.lineTo(graphBoxMarginLeft + 0, graphBoxMarginTop + 200);
|
||
ctx.moveTo(graphBoxMarginLeft + 0, graphBoxMarginTop + 200);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, graphBoxMarginTop + 200);
|
||
ctx.stroke();
|
||
|
||
/*
|
||
// ---------------------------------------------------- ZEICHNEN DER LIMITS ----------------------------------------------------
|
||
*/
|
||
|
||
// Textbeschriftungen
|
||
ctx.fillText("0h", graphBoxMarginLeft - 10, graphBoxMarginTop + 200);
|
||
ctx.textBaseline = "top";
|
||
ctx.fillText("0FFFFh", graphBoxMarginLeft - 10, graphBoxMarginTop);
|
||
|
||
if (true) { // CCR0 wird immer gezeichnet
|
||
|
||
// gestrichelte Linie zeichnen, je nach TAxCCR0
|
||
ctx.setLineDash([5, 5]);
|
||
ctx.beginPath();
|
||
ctx.moveTo(graphBoxMarginLeft, graphBoxMarginTop + TAxCCR0_pos);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, graphBoxMarginTop + TAxCCR0_pos);
|
||
ctx.stroke();
|
||
|
||
// Text zur gestrichelten Linie TAxCCR0
|
||
ctx.textBaseline = "middle";
|
||
ctx.fillText("TAxCCR0", graphBoxMarginLeft - 10, graphBoxMarginTop + TAxCCR0_pos);
|
||
|
||
}
|
||
|
||
if (true) {
|
||
|
||
// gestrichelte Linie zeichnen, je nach TAxCCR1
|
||
ctx.setLineDash([5, 5]);
|
||
ctx.beginPath();
|
||
ctx.moveTo(graphBoxMarginLeft, graphBoxMarginTop + TAxCCR1_pos);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, graphBoxMarginTop + TAxCCR1_pos);
|
||
ctx.stroke();
|
||
|
||
// Text zur gestrichelten Linie TAxCCR1
|
||
ctx.textBaseline = "middle";
|
||
ctx.fillText("TAxCCR1", graphBoxMarginLeft - 10, graphBoxMarginTop + TAxCCR1_pos);
|
||
|
||
}
|
||
if (true) {
|
||
|
||
// gestrichelte Linie zeichnen, je nach TAxCCR2
|
||
ctx.setLineDash([5, 5]);
|
||
ctx.beginPath();
|
||
ctx.moveTo(graphBoxMarginLeft, graphBoxMarginTop + TAxCCR2_pos);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, graphBoxMarginTop + TAxCCR2_pos);
|
||
ctx.stroke();
|
||
|
||
// Text zur gestrichelten Linie TAxCCR1
|
||
ctx.textBaseline = "middle";
|
||
ctx.fillText("TAxCCR2", graphBoxMarginLeft - 10, graphBoxMarginTop + TAxCCR2_pos);
|
||
|
||
}
|
||
|
||
/*
|
||
// ---------------------------------------------------- INITIALISIERUNG DER SCHLEIFE ----------------------------------------------------
|
||
*/
|
||
|
||
drawnWidth = 0; // gibt die Breite des gezeichneten Graphen an - neue Elemente knüpfen hier an!
|
||
drawnWidthTAxCCR0 = 0; // Extra-Breiten-Zähler, wird relevant später, entspricht in Modus 0 und 2 drawnWidth
|
||
drawnWidthTAxCCR1 = 0; // Extra-Breiten-Zähler, wird relevant später
|
||
drawnWidthTAxCCR2 = 0; // Extra-Breiten-Zähler, wird relevant später
|
||
limiter = 0; // begrenzt die maximale Anzahl gezeichneter Elemente (Fallback, falls Drawn-Width nie erreicht wird und der Browser abstürzt)
|
||
timer = 0;
|
||
|
||
//die targetHeight ist im Cont. Mode 200-200=0 (Abstand vom Oberen Ende der X-Achse!)
|
||
if (timerMode == 2) {
|
||
targetHeight = 0;
|
||
} else {
|
||
targetHeight = TAxCCR0_pos;
|
||
}
|
||
|
||
/*
|
||
// ---------------------------------------------------- BEGINN DER SCHLEIFE ----------------------------------------------------
|
||
*/
|
||
|
||
// Haupt-Schleife: Will erreichen, dass Graph mind. 1400 Pixel breit; Limiter begrenzt, dass Browser nicht abstürzt; timer = halted = keine Zeichnung
|
||
while (drawnWidth < 1600 && limiter <= 100 && timerMode !== 0) {
|
||
|
||
ctx.beginPath();
|
||
ctx.setLineDash([]); // normale Linien
|
||
ctx.strokeStyle = colors["Blue 3"];
|
||
ctx.lineWidth = 3;
|
||
|
||
// Schräge Linie (Rampe) Breite: Von 0 bis (200 - targetHeight) * ramp
|
||
ctx.moveTo(graphBoxMarginLeft + drawnWidth, 200 + graphBoxMarginTop);
|
||
|
||
// Schnittpunkt mit 0h ist schonmal ein Tal
|
||
graphIntersections.push({
|
||
"x": drawnWidth,
|
||
"y": (200 - targetHeight),
|
||
"limitType": "TAIFG",
|
||
"edgeType": "through"
|
||
});
|
||
|
||
// | Zielbreite | X-Pos des TAxCCRn-Schnittpunkts
|
||
drawnWidthTAxCCR1 = drawnWidth + (200 - TAxCCR1_pos) * ramp;
|
||
drawnWidthTAxCCR2 = drawnWidth + (200 - TAxCCR2_pos) * ramp;
|
||
|
||
drawnWidthTAxCCR0 = drawnWidth + (200 - TAxCCR0_pos) * ramp;
|
||
|
||
// Zielbreite für dieses Segment - in Modus 2 doppelt so breit! (Addition später)
|
||
drawnWidth = drawnWidth + (200 - targetHeight) * ramp;
|
||
|
||
ctx.lineTo(graphBoxMarginLeft + drawnWidth, graphBoxMarginTop + targetHeight);
|
||
|
||
// Im Timer Mode 2 wird eine fallende Rampe statt eines geraden Abschlusses benötigt
|
||
if (timerMode == 3) {
|
||
// die Zielbreite ist hier doppelt so groß, da 2*Rampe
|
||
// | Zweite Rampe |
|
||
ctx.moveTo(graphBoxMarginLeft + drawnWidth + (200 - targetHeight) * ramp, 200 + graphBoxMarginTop);
|
||
ctx.lineTo(graphBoxMarginLeft + drawnWidth, graphBoxMarginTop + targetHeight);
|
||
|
||
// Schnittpunkt mit TAxCCR0 bei Berühren
|
||
graphIntersections.push({
|
||
"x": drawnWidth,
|
||
"y": TAxCCR0_pos,
|
||
"limitType": "TAxCCR0",
|
||
"edgeType": "peak"
|
||
});
|
||
|
||
// Finales Aufaddieren der tatsächlichen Größe (also 2*Rampe)
|
||
drawnWidth = drawnWidth + (200 - targetHeight) * ramp;
|
||
|
||
// Schnittpunkt mit TAxCCR1 (kann von TAxCCR1_pos abgezwackt werden)
|
||
if (targetHeight < TAxCCR1_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR1,
|
||
"y": TAxCCR1_pos,
|
||
"limitType": "TAxCCR1",
|
||
"edgeType": "rise"
|
||
});
|
||
}
|
||
|
||
// Schnittpunkt mit TAxCCR2 (kann von TAxCCR2_pos abgezwackt werden)
|
||
if (targetHeight < TAxCCR2_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR2,
|
||
"y": TAxCCR2_pos,
|
||
"limitType": "TAxCCR2",
|
||
"edgeType": "rise"
|
||
});
|
||
}
|
||
|
||
// für die fallende Taktflanke im Modus 2 wird sich die Symmetrie zu Nutze gemacht:
|
||
// auf die gezeichnete Breite (also Scheitel des Dreiecks) wird die Differenz aus TAxCCR0 und TAxCCR1 addiert! -> Betrag zum Scheitel (Quasi Spiegelung -> Fall)
|
||
if (targetHeight < TAxCCR1_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidth - (200 - TAxCCR1_pos) * ramp,
|
||
"y": TAxCCR1_pos,
|
||
"limitType": "TAxCCR1",
|
||
"edgeType": "fall"
|
||
});
|
||
}
|
||
|
||
if (targetHeight < TAxCCR2_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidth - (200 - TAxCCR2_pos) * ramp,
|
||
"y": TAxCCR2_pos,
|
||
"limitType": "TAxCCR2",
|
||
"edgeType": "fall"
|
||
});
|
||
}
|
||
|
||
} else { // Wenn ein gerader Abschluss benötigt wird
|
||
|
||
// gerader Abschluss mit der Höhe targetHeight
|
||
ctx.moveTo(graphBoxMarginLeft + drawnWidth, 200 + graphBoxMarginTop);
|
||
ctx.lineTo(graphBoxMarginLeft + drawnWidth, graphBoxMarginTop + targetHeight);
|
||
|
||
// Schnittpunkt (Peak) mit TAxCCR0 bei steigender Flanke (X-Pos ist die gezeichnete Breite, da TAxCCR0 die Breite vorgibt), ist drawnWidthTAxCCR0 != drawnWidth, handelt es sich nicht um den gleichen Punkt, sondern der Graph ist noch ein Bewegung -> Rise
|
||
if (drawnWidthTAxCCR0 == drawnWidth) {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR0,
|
||
"y": TAxCCR0_pos,
|
||
"limitType": "TAxCCR0",
|
||
"edgeType": "peak"
|
||
});
|
||
} else {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR0,
|
||
"y": TAxCCR0_pos,
|
||
"limitType": "TAxCCR0",
|
||
"edgeType": "rise"
|
||
});
|
||
}
|
||
|
||
// Schnittpunkt mit TAxCCR1 bei steigender Flanke (X-Pos ist die gezeichnete Breite)
|
||
if (targetHeight < TAxCCR1_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR1,
|
||
"y": TAxCCR1_pos,
|
||
"limitType": "TAxCCR1",
|
||
"edgeType": "rise"
|
||
});
|
||
}
|
||
|
||
if (targetHeight < TAxCCR2_pos) {
|
||
graphIntersections.push({
|
||
"x": drawnWidthTAxCCR2,
|
||
"y": TAxCCR2_pos,
|
||
"limitType": "TAxCCR2",
|
||
"edgeType": "rise"
|
||
});
|
||
}
|
||
}
|
||
ctx.stroke();
|
||
ctx.strokeStyle = colors["Dark 5"];
|
||
ctx.lineWidth = 1;
|
||
limiter++;
|
||
}
|
||
|
||
/*
|
||
// ---------------------------------------------------- ENDE DER SCHLEIFE ----------------------------------------------------
|
||
*/
|
||
|
||
// sicherheitshalber das Array nach X-Koordinate sortieren
|
||
graphIntersections.sort(function(a, b) {
|
||
return a.x - b.x
|
||
});
|
||
|
||
/*
|
||
// ---------------------------------------------------- X-ACHSENBESCHRIFTUNG ----------------------------------------------------
|
||
*/
|
||
|
||
ctx.textAlign = "center";
|
||
ctx.font = "15px Roboto Mono";
|
||
x_before = -200;
|
||
graphIntersections.forEach(element => {
|
||
//legacy: nur TAIFG als Orientierung nutzen
|
||
//if (element["limitType"] === "TAxCCR2") {
|
||
|
||
t = (element["x"] / 3.051757812 / (zoom * 0.01)).toFixed(2);
|
||
|
||
if ((element["x"] - x_before) > 100) {
|
||
|
||
ctx.fillText(t + "ms", graphBoxMarginLeft + element["x"], graphBoxMarginTop + 225);
|
||
ctx.setLineDash([]); // normale Linien
|
||
ctx.beginPath();
|
||
ctx.moveTo(graphBoxMarginLeft + element["x"], graphBoxMarginTop + 210);
|
||
ctx.lineTo(graphBoxMarginLeft + element["x"], graphBoxMarginTop + 200);
|
||
ctx.stroke();
|
||
x_before = element["x"];
|
||
}
|
||
});
|
||
|
||
ctx.font = "20px Roboto Mono";
|
||
|
||
// die zuvor festgelegte Fiterliste wird nun auf die ermittelten Schnittpunkte angewandt
|
||
// ist bspw. für TAIFG gar kein TAIE gesetzt, fliegt es hier nun raus
|
||
graphIntersections = graphIntersections.filter(item => filterList.includes(item.limitType));
|
||
|
||
/*
|
||
// --------------------------------------- ZEICHNEN DER SCHNITTPUNKTE/INTERRUPTS --------------------------------------------
|
||
*/
|
||
|
||
// Parameter für jeden Interrupt-Kanal
|
||
const drawParameters = [{
|
||
type: "TAIFG",
|
||
label: "TAIFG",
|
||
startY: 310
|
||
},
|
||
{
|
||
type: "TAxCCR0",
|
||
label: "EQU0",
|
||
startY: 410,
|
||
endY: 960,
|
||
dashed: true
|
||
},
|
||
{
|
||
type: "TAxCCR1",
|
||
label: "EQU1",
|
||
startY: 510,
|
||
endY: 960,
|
||
dashed: true
|
||
},
|
||
{
|
||
type: "TAxCCR2",
|
||
label: "EQU2",
|
||
startY: 610,
|
||
endY: 960,
|
||
dashed: true
|
||
}
|
||
];
|
||
|
||
graphBoxMarginLeft = 110;
|
||
const boxHeight = 50;
|
||
|
||
// Textausrichtung für alle Boxen zentral setzen
|
||
ctx.textAlign = "right";
|
||
ctx.textBaseline = "bottom";
|
||
ctx.font = "20px Roboto Mono";
|
||
|
||
// --- START DER DEDUPLIZIERENDEN SCHLEIFE ---
|
||
for (let j = 0; j < drawParameters.length; j++) {
|
||
const params = drawParameters[j];
|
||
|
||
// 1. Grundlinien (Achsen) zeichnen
|
||
ctx.beginPath();
|
||
ctx.setLineDash([]); // Normale Linien (Achsen)
|
||
ctx.moveTo(graphBoxMarginLeft, params.startY);
|
||
ctx.lineTo(graphBoxMarginLeft, params.startY + boxHeight); // Y-Achse
|
||
ctx.moveTo(graphBoxMarginLeft, params.startY + boxHeight);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, params.startY + boxHeight); // X-Achse
|
||
ctx.stroke();
|
||
|
||
// 2. Textbeschriftung links (TAIFG, EQU0, EQU1, EQU2)
|
||
ctx.fillText(params.label, graphBoxMarginLeft - 10, params.startY + boxHeight);
|
||
|
||
// 3. Schnittpunkte verarbeiten und zeichnen
|
||
graphIntersections.forEach(element => {
|
||
if (element["limitType"] === params.type) {
|
||
|
||
// Schnittpunktlinie (V-Linie)
|
||
ctx.beginPath();
|
||
ctx.setLineDash([]); // Normale Linie
|
||
ctx.strokeStyle = colors["Blue 3"];
|
||
ctx.lineWidth = 3;
|
||
const xPos = element["x"] + graphBoxMarginLeft;
|
||
|
||
ctx.moveTo(xPos, boxHeight + params.startY);
|
||
ctx.lineTo(xPos, params.startY);
|
||
|
||
// Beschriftung des Schnittpunkts (edgeType)
|
||
ctx.fillText(element["edgeType"], xPos, params.startY);
|
||
ctx.stroke();
|
||
ctx.strokeStyle = colors["Dark 5"];
|
||
ctx.lineWidth = 1;
|
||
|
||
// 4. Gestrichelte Linien zur Orientierung (nur für CCRn)
|
||
if (params.dashed) {
|
||
ctx.setLineDash([5, 15]); // Gestrichelte Linie
|
||
ctx.strokeStyle = colors["Dark 1"];
|
||
ctx.beginPath();
|
||
ctx.moveTo(xPos, graphBoxMarginTop + element["y"]);
|
||
ctx.lineTo(xPos, params.endY); // Endpunkt ist variabel (endY)
|
||
ctx.stroke();
|
||
ctx.strokeStyle = colors["Dark 5"];
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/*
|
||
// --------------------------------------- DEFINITION DES OUTPUT-PATTERN --------------------------------------------
|
||
*/
|
||
|
||
// das Outmode-Pattern kann fixe Werte, Toggle sowie Set/Reset speichern
|
||
// outputModePatterns[i] beschreibt immer den verwendeten Timer-Mode
|
||
// outputModePatterns[i]["default"/"up-down"] unterscheidet in up/down oder cont/up weil z.b. im Output Mode 3: Set/Reset im Up/down bei ersterem Erreichen des Limits kein Interrupt ausgelöst wird
|
||
// outputModePatterns[i]["default"/"up-down"]["TAxCCR0"/"TAxCCRn"] unterscheidet zwischen CCR0 und CCRn
|
||
// WICHTIG: CCR0 gilt als CCR0 und CCRn gleichzeitig!
|
||
// outputModePatterns[i]["default"/"up-down"]["TAxCCR0"/"TAxCCRn"]["through"/"rise"/"peak"/"fall"] unterscheidet zwischen den "Flanken-Typen"
|
||
// outputModePatterns[i]["default"/"up-down"]["TAxCCR0"/"TAxCCRn"]["through"/"rise"/"peak"/"fall"][0/1/2] setzt die tatsächlichen Werte!
|
||
// [0] steht für "Wert ist aktuell false" und [1] steht für "Wert ist aktuell true"
|
||
// ein Toggle lässt sich also über [true, false] realisiern, ein bedinungsloser Reset durch [false, false] usw..
|
||
// WICHTIG!: [2] beschreibt einen fixen Werrt, der bei Set und Reset zum Tragen kommt. Hier wird angegeben, wie sich die Simulation bei Erreichen des Wertes dann verhalten soll
|
||
|
||
// Grafiken nachgebaut (beschreibt die Hardware falsch, da Dokumentationsfehler!)
|
||
|
||
/*
|
||
outputModePatterns = {
|
||
0: // 1
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
}
|
||
}
|
||
},
|
||
1: // 1
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true]
|
||
}
|
||
}
|
||
},
|
||
2: // 2
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": true
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [false, true],
|
||
"fall": [true, true],
|
||
"bistable": true
|
||
}
|
||
}
|
||
},
|
||
3: // 3
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": true
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [true, true],
|
||
"bistable": true
|
||
}
|
||
}
|
||
},
|
||
4: // 4
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false]
|
||
}
|
||
}
|
||
},
|
||
5: // 5
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false]
|
||
}
|
||
}
|
||
},
|
||
6: // 6
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": false
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [false, true],
|
||
"fall": [false, false],
|
||
"bistable": false
|
||
}
|
||
}
|
||
},
|
||
7: // 7
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": false
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, false],
|
||
"bistable": false
|
||
}
|
||
}
|
||
},
|
||
};
|
||
|
||
// am Text/der Tabelle Orientiert (so korrekt nachgemessen)
|
||
|
||
*/
|
||
|
||
outputModePatterns = {
|
||
0: // 1
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
}
|
||
}
|
||
},
|
||
1: // 1
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true]
|
||
}
|
||
}
|
||
},
|
||
2: // 2
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": true
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": true
|
||
}
|
||
}
|
||
},
|
||
3: // 3
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": true
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": true
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": true
|
||
}
|
||
}
|
||
},
|
||
4: // 4
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false]
|
||
}
|
||
}
|
||
},
|
||
5: // 5
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false]
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [false, true],
|
||
"peak": [false, true],
|
||
"fall": [false, true]
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false]
|
||
}
|
||
}
|
||
},
|
||
6: // 6
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": false
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [true, false],
|
||
"peak": [true, false],
|
||
"fall": [true, false],
|
||
"bistable": false
|
||
}
|
||
}
|
||
},
|
||
7: // 7
|
||
{
|
||
"default": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": false
|
||
}
|
||
},
|
||
"up-down": {
|
||
"TAxCCR0": {
|
||
"rise": [true, true],
|
||
"peak": [true, true],
|
||
"fall": [true, true],
|
||
"bistable": false
|
||
},
|
||
"TAxCCRn": {
|
||
"rise": [false, false],
|
||
"peak": [false, false],
|
||
"fall": [false, false],
|
||
"bistable": false
|
||
}
|
||
}
|
||
},
|
||
};
|
||
|
||
/*
|
||
// --------------------------------------- SCHNITTPUNKTE -> OUTPUT-LEVELS --------------------------------------------
|
||
*/
|
||
|
||
// aus den Zuvor ermittelten Schnittpunkten wird nun unter Zuhilfenahme der Modus-Map das Output-Level erstellt
|
||
|
||
// Hier wird outputLevels als Array von Arrays initialisiert,
|
||
// um outputLevels[0], outputLevels[1], outputLevels[2] zu haben
|
||
// dieser Speichert den zeitlichen Verlauf des Ausgabegraphen
|
||
const outputLevels = [
|
||
[], // Index 0: Inhalt von outputLevel0
|
||
[], // Index 1: Inhalt von outputLevel1
|
||
[], // Index 2: Inhalt von outputLevel2
|
||
];
|
||
|
||
// im OutMode0 (also OUT) wird nur ein fixer Wert gegeben, der mit dem OUT-Bit festgelegt wird
|
||
startValue0 = startValue;
|
||
if (outputMode0 == 0) {
|
||
startValue0 = OUT0;
|
||
}
|
||
startValue1 = startValue;
|
||
if (outputMode1 == 0) {
|
||
startValue1 = OUT1;
|
||
}
|
||
startValue2 = startValue;
|
||
if (outputMode2 == 0) {
|
||
startValue2 = OUT2;
|
||
}
|
||
|
||
// Zustandsspeicher für jede Iteration jedes Kanals
|
||
// früher wurden die Werte für jeden Kanal einzeln gespeichert (also toggle0, toggle1..., i0, i1...) das ist hiermit schöner gelöst
|
||
const outputStates = {
|
||
0: {
|
||
toggle: startValue0,
|
||
x_from: 0,
|
||
i: 0
|
||
},
|
||
1: {
|
||
toggle: startValue1,
|
||
x_from: 0,
|
||
i: 0
|
||
},
|
||
2: {
|
||
toggle: startValue2,
|
||
x_from: 0,
|
||
i: 0
|
||
}
|
||
};
|
||
|
||
// diese Funktion verwendet das Pattern um den Output-Verlauf zu erstellen
|
||
// es wird durch die Iteration durch alle Schnittpunkte aufgerufen
|
||
function useOutputModePattern(outputIndex, element, mode, timerMode, enableBistable) {
|
||
// Hole den aktuellen Zustand für diesen Output-Kanal
|
||
const state = outputStates[outputIndex];
|
||
|
||
// Bestimme den Output-Case
|
||
// im Up/down-Mode gibts die Besonderheit, dass teilweise bei fallenden Flanken kein Interrupt mehr ausgelöst wird,
|
||
// bzw. die Symmetrie absichtlich verletzt wird
|
||
const outputCase = (timerMode === 3) ? "up-down" : "default";
|
||
|
||
// Hole das Pattern-Array
|
||
const pattern = outputModePatterns[mode][outputCase][element["limitType"]]?.[element["edgeType"]];
|
||
|
||
if (!pattern) return; // Abbruch, wenn das Pattern nicht existiert
|
||
|
||
const x_to = element["x"];
|
||
|
||
// schreibe den Wert nun in das ouputLevel-Array
|
||
outputLevels[outputIndex].push({
|
||
"x_from": state.x_from,
|
||
"x_to": x_to,
|
||
"level": state.toggle
|
||
});
|
||
|
||
// Werte setzen für die nächste Iteration
|
||
state.x_from = x_to;
|
||
if (outputModePatterns[mode][outputCase][element["limitType"]]["bistable"] !== undefined && enableBistable) {
|
||
state.toggle = outputModePatterns[mode][outputCase][element["limitType"]]["bistable"];
|
||
} else {
|
||
state.toggle = state.toggle ? pattern[1] : pattern[0];
|
||
}
|
||
|
||
}
|
||
|
||
console.log(graphIntersections);
|
||
|
||
// iteriere durh alle Schnittpunkte
|
||
// wichtige Besonderheit bei CCR0: dieser wird sowohl als CCR0, als auch CCRn behandelt! CCR0 ist jedoch dominant
|
||
graphIntersections.forEach(element => {
|
||
// Speichere den Original-limitType, da er von der Logik geändert wird
|
||
const originalLimitType = element["limitType"];
|
||
|
||
// 1. Logik für Output 0 (CCR0 als CCRn)
|
||
if (originalLimitType === "TAxCCR0") {
|
||
// Durchlauf (als CCRn mit Nachrang)
|
||
element["limitType"] = "TAxCCRn";
|
||
useOutputModePattern(0, element, outputMode0, timerMode, true);
|
||
|
||
}
|
||
|
||
// 2. Logik für Output 1 (TAxCCR0 oder TAxCCR1)
|
||
if (["TAxCCR0", "TAxCCR1"].includes(originalLimitType)) {
|
||
if (originalLimitType === "TAxCCR1") {
|
||
element["limitType"] = "TAxCCRn";
|
||
} else {
|
||
element["limitType"] = originalLimitType; // TAxCCR0
|
||
}
|
||
useOutputModePattern(1, element, outputMode1, timerMode);
|
||
}
|
||
|
||
// 3. Logik für Output 2 (TAxCCR0 oder TAxCCR2)
|
||
if (["TAxCCR0", "TAxCCR2"].includes(originalLimitType)) {
|
||
if (originalLimitType === "TAxCCR2") {
|
||
element["limitType"] = "TAxCCRn";
|
||
} else {
|
||
element["limitType"] = originalLimitType; // TAxCCR0
|
||
}
|
||
useOutputModePattern(2, element, outputMode2, timerMode);
|
||
}
|
||
|
||
// Stelle den limitType wieder her, bevor die nächste Iteratiion der Schleife startet
|
||
element["limitType"] = originalLimitType;
|
||
});
|
||
|
||
/*
|
||
// --------------------------------------- AUSGABE DER OUTPUT-LEVELS --------------------------------------------
|
||
*/
|
||
|
||
// Parameter für die Anzeigeboxen
|
||
const outputDrawParameters = [{
|
||
label: "Output0",
|
||
startY: 710,
|
||
index: 0
|
||
},
|
||
{
|
||
label: "Output1",
|
||
startY: 810,
|
||
index: 1
|
||
},
|
||
{
|
||
label: "Output2",
|
||
startY: 910,
|
||
index: 2
|
||
}
|
||
];
|
||
|
||
graphBoxMarginLeft = 110;
|
||
|
||
// Einmaliges Setzen des Text-Kontextes
|
||
ctx.textAlign = "right";
|
||
ctx.textBaseline = "bottom";
|
||
ctx.font = "20px Roboto Mono";
|
||
|
||
// Iteriere durch alle zu zeichnenden Elemente
|
||
outputDrawParameters.forEach(params => {
|
||
|
||
// Lokale Variablen für diese Box
|
||
const graphBoxMarginTop = params.startY;
|
||
let level_prev = undefined; // Zustand (level_prev) wird für jeden Output separat initialisiert
|
||
|
||
// Zeichnet ein einzelnes Level und wird augerufen, wenn eine Linie (x_from -> x_to) gezeichnet werden soll
|
||
function drawOutput(element) {
|
||
ctx.beginPath();
|
||
ctx.setLineDash([]); // Normale Linien (für den Output-Verlauf)
|
||
ctx.strokeStyle = colors["Blue 3"];
|
||
ctx.lineWidth = 3;
|
||
|
||
// Horizontaler Verlauf
|
||
if (element["level"]) {
|
||
// HIGH Level
|
||
ctx.moveTo(element["x_from"] + graphBoxMarginLeft, graphBoxMarginTop);
|
||
ctx.lineTo(element["x_to"] + graphBoxMarginLeft, graphBoxMarginTop);
|
||
} else {
|
||
// LOW Level
|
||
ctx.moveTo(element["x_from"] + graphBoxMarginLeft, boxHeight + graphBoxMarginTop);
|
||
ctx.lineTo(element["x_to"] + graphBoxMarginLeft, boxHeight + graphBoxMarginTop);
|
||
}
|
||
|
||
// Vertikale Flanke (wenn sich der Pegel ändert)
|
||
if ((level_prev !== element["level"]) && level_prev !== undefined) {
|
||
ctx.moveTo(element["x_from"] + graphBoxMarginLeft, boxHeight + graphBoxMarginTop);
|
||
ctx.lineTo(element["x_from"] + graphBoxMarginLeft, graphBoxMarginTop);
|
||
}
|
||
|
||
// Zustand für die nächste Iteration speichern
|
||
level_prev = element["level"];
|
||
|
||
ctx.stroke();
|
||
ctx.strokeStyle = colors["Dark 5"];
|
||
ctx.lineWidth = 1;
|
||
}
|
||
|
||
// das eigentliche Zeichnen der Box
|
||
|
||
// 1. Grundlinien (Achsen) zeichnen
|
||
ctx.beginPath();
|
||
ctx.setLineDash([5, 5]); // Gestrichelte Linien für die Grundlinie (wie im Original)
|
||
|
||
ctx.moveTo(graphBoxMarginLeft, graphBoxMarginTop);
|
||
ctx.lineTo(graphBoxMarginLeft, graphBoxMarginTop + boxHeight); // Y-Achse
|
||
ctx.moveTo(graphBoxMarginLeft, graphBoxMarginTop + boxHeight);
|
||
ctx.lineTo(graphBoxMarginLeft + 800, graphBoxMarginTop + boxHeight); // X-Achse
|
||
|
||
ctx.stroke();
|
||
ctx.setLineDash([]); // Gestrichelte Linien wieder deaktivieren
|
||
|
||
// 2. Textbeschriftung links (Output0, Output1, Output2)
|
||
ctx.fillText(params.label, graphBoxMarginLeft - 10, graphBoxMarginTop + boxHeight);
|
||
|
||
// 3. Output-Segmente zeichnen mit der definierten Funktion
|
||
outputLevels[params.index].forEach(element => {
|
||
drawOutput(element);
|
||
});
|
||
|
||
});
|
||
|
||
console.log(outputLevels);
|
||
|
||
// ende des Canvas! lösche alles, was breiter als 900px nach rechts hinausläuft
|
||
ctx.clearRect(900, 0, canvas.width, canvas.height);
|
||
|
||
/*
|
||
// --------------------------------------- TEXT-ERKLÄRUNG --------------------------------------------
|
||
*/
|
||
|
||
// Dies sind die Timer/Output-Mode in Worten
|
||
const timerModeMap = {
|
||
0: "Hold",
|
||
1: "Up",
|
||
2: "Continuous",
|
||
3: "Up-/Down"
|
||
};
|
||
|
||
const outputModeMap = {
|
||
0: "Output",
|
||
1: "Set",
|
||
2: "Toggle-/Reset",
|
||
3: "Set-/Reset",
|
||
4: "Toggle",
|
||
5: "Reset",
|
||
6: "Toggle-/Set",
|
||
7: "Reset-/Set"
|
||
};
|
||
|
||
const tasselMap = {
|
||
0: "TAxCLK (1 MHz)",
|
||
1: "ACLK",
|
||
2: "SMCLK (32 kHz)",
|
||
3: "INCLK"
|
||
};
|
||
|
||
// für jede dieser Werte gibts eine Erlärung, die zunächst gesammelt wird
|
||
explanatoryText = {
|
||
"TAxCTL": "",
|
||
"TAxCCTL0": "",
|
||
"TAxCCTL1": "",
|
||
"TAxCCTL2": ""
|
||
};
|
||
|
||
// Erklärung des Timers
|
||
explanatoryText["TAxCTL"] += "Der Timer befindet sich im <b>" + timerModeMap[timerMode] + "-Mode</b> mit einem </b>Teilerwert von <b>1/" + rampOriginal + "</b>. Das Timer_A-Interrupt-Enable (<b>TAIE</b>) ist " + TAIE + ". Timer-Quelle: <b>" + tasselMap[TASSEL] + "</b>.";
|
||
// Timer-Mode mit optionalem OUTn-Wert
|
||
explanatoryText["TAxCCTL0"] += "CCTL0 befindet sich im <b>" + outputModeMap[outputMode0] + "-Modus</b> mit dem OUT-Wert <b>" + OUT0 + "</b>.";
|
||
|
||
if (outputModePatterns[outputMode0]["default"]["TAxCCRn"]["bistable"] !== undefined) {
|
||
explanatoryText["TAxCCTL0"] += "<br><b>Sie haben einen Sonderfall entdeckt! Weil CCRn == CCR0, erzeugt der OUT0-Kanal in diesem Output-Modus ein unerwartetes Signal...</b>";
|
||
}
|
||
|
||
explanatoryText["TAxCCTL1"] += "CCTL1 befindet sich im <b>" + outputModeMap[outputMode1] + "-Modus</b> mit dem OUT-Wert <b>" + OUT1 + "</b>.";
|
||
explanatoryText["TAxCCTL2"] += "CCTL2 befindet sich im <b>" + outputModeMap[outputMode2] + "-Modus</b> mit dem OUT-Wert <b>" + OUT2 + "</b>.";
|
||
|
||
// schreibe die Erklärungen in die zugehörige Box
|
||
Object.entries(explanatoryText).forEach(([key, value]) => {
|
||
document.getElementById("explain-" + key + "-text").innerHTML = value;
|
||
});
|
||
}
|
||
</script>
|
||
|