Stap 24: ECG-Simulator - Arduino Sketch
// ***************************************************************************************************ECG-SIMULATOR
//
Doel: simuleren het normale sinus ritme ECG signaal (3-leads RA, LA, RL)
//
Achtergrond:
//
In normale elektrocardiogram (ECG of EKG als je Duits), drie leads make-up de
Einthoven van de driehoek. Twee leidt zijn geplakt is aan de rechter en linker kant van de borst hierboven
het hart (RA = rechterarm, LA = linker arm) en één loden is geplakt is aan de onderborst, meestal
op de rechter heup (RL = rechterbeen).
//
Het is belangrijk om te weten dat deze ECG-signalen millivolts in amplitude. Dit kan worden bereikt door
het voeden van de D/A-omzetter via een verdeler van de spanning om naar de millivolt niveaus.
//
//
Het ECG-signaal:
//
Ik vond een geschikte ECG golfvorm van het internet. Hier is hoe ik een afbeelding uit omgezet mijn
beeldscherm aan een array van de taal C A/D waarden, elke verdeelde 1,00 msec uit elkaar.
//
A. het schermschot van golfvorm met behulp van de gratis scherm vangen programma MWSNAP
http://www.mirekw.com/winfreeware/mwsnap.html
//
B. de golfvorm van de jpeg met behulp van de gratis analoge programma ENGAUGE digitaliseren
http://digitizer.sourceforge.net/
//
//
C: Ik schreef een Python programma om te converteren van de nogal onregelmatig monsters van ENGAUGE
verdeeld naar een matrix van waarden 1.0 milliseconden uit elkaar met behulp van lineaire interpolatie.
Dan heb ik een tekstbestand waar deze gegevenspunten deel uit van een array van de taal C maakten
Construct; dat wil zeggen de gegevenspunten worden C initializers.
//
D: knippen-en-plakken van de tekst bestand de C data array met initializers in de
Arduino schets hieronder.
//
//
Arduino middelen:
//
Digitale uitvoer # 9 - chip Selecteer de SPI-poort van de 7-segment display (laag te selecteren)
Digitale uitgang # 10 - chip selecteren voor D/A converter (laag te selecteren)
Digitale uitgang # 11 - SDI gegevens naar de D/A converter (SPI interface)
Digitale uitgang # 13 - SCK klok naar de D/A converter (SPI interface)
//
Analoge Input # 0 - centrum wisser pin van 5 k ohm pot (hartslag aanpassen)
//
Ik volgde de Timer2-installatie zoals beschreven door Sebastian Wallin
http://popdevelop.com/2010/04/Mastering-timer-interrupts-on-the-Arduino/
//
Mij troep opwaarts de SPI interface volgens de uitstekende instructies van Australische John Boxall,
prachtige website heeft veel uitstekende Arduino tutorials:
http://tronixstuff.WordPress.com/
//
Programmeur: James P Lynch
lynch007
//
// ***************************************************************************************************
#include "SPI.h" / / ondersteunt de SPI interface naar de D/A-omzetter en de 7-segment display
#include < Wire.h > / / noodzaak van de draad bibliotheek
diverse constanten die worden gebruikt door de golfvorm generator
#define INIT 0
#define IDLE 1
#define QRS 2
#define vier 4
#define 3
#define twee 2
#define één 1-inch
// *******************************************************************************************
y_data [543] - gedigitaliseerd ecg golfvorm, bemonsterd op 1.0 msec
//
Golfvorm wordt geschaald naar een 12-bit D/A converter (0.. 4096)
//
Een 60 beat/min ECG zou vereisen dit golfvorm (543 monsters) plus 457 monsters
van de eerste y_data [0] waarde van 939.
//
// *********************************************************************************************
Const korte y_data [] {} =
939, 940, 941, 942, 944, 945, 946, 947, 951, 956,
962, 967 973, 978, 983, 989, 994, 1000, 1005, 1015,
1024 1034, 1043, 1053, 1062, 1075, 1087, 1100, 1112, 1121,
1126 1131, 1136, 1141, 1146, 1151, 1156, 1164, 1172, 1179,
1187 1194, 1202, 1209, 1216, 1222, 1229, 1235, 1241, 1248,
1254 1260, 1264, 1268, 1271, 1275, 1279, 1283, 1287, 1286,
1284 1281, 1279, 1276, 1274, 1271, 1268, 1266, 1263, 1261,
1258 1256, 1253, 1251, 1246, 1242, 1237, 1232, 1227, 1222,
1218 1215, 1211, 1207, 1203, 1199, 1195, 1191, 1184, 1178,
1171 1165, 1159, 1152, 1146, 1141, 1136, 1130, 1125, 1120,
1115 1110, 1103, 1096, 1088, 1080, 1073, 1065, 1057, 1049,
1040, 1030, 1021, 1012, 1004, 995, 987, 982, 978, 974,
970 966, 963, 959, 955, 952, 949, 945, 942, 939,
938 939, 940, 941, 943, 944, 945, 946, 946, 946,
946 946, 946, 946, 946, 947, 950, 952, 954, 956,
958 960, 962, 964, 965, 965, 965, 965, 965, 965,
963 960, 957, 954, 951, 947, 944, 941, 938, 932,
926 920, 913, 907, 901, 894, 885, 865, 820, 733,
606 555, 507, 632, 697, 752, 807, 896, 977, 1023,
1069, 1127 1237, 1347, 1457, 2085, 2246, 2474, 2549, 2595,
2641, 2695, 3083 3135, 3187, 3217, 3315, 3403, 3492, 3581,
3804 3847, 3890, 3798, 3443, 3453, 3297, 3053, 2819, 2810,
2225 2258, 1892, 1734, 1625, 998, 903, 355, 376, 203,
30, 33, 61, 90, 119, 160, 238, 275, 292, 309,
325 343, 371, 399, 429, 484, 542, 602, 652, 703,
758 802, 838, 856, 875, 895, 917, 938, 967, 1016,
1035 1041, 1047, 1054, 1060, 1066, 1066, 1064, 1061, 1058,
1056 1053, 1051, 1048, 1046, 1043, 1041, 1038, 1035, 1033,
1030, 1028, 1025, 1022, 1019, 1017, 1014, 1011, 1008, 1006,
1003, 1001, 999, 996, 998, 994, 993, 991, 990, 988,
986, 985, 983, 981, 978, 976, 973, 971, 968, 966,
963 963, 963, 963, 963, 963, 963, 963, 963, 963
963 963, 963, 963, 963, 963, 963, 963, 963, 963
964 965, 966, 967, 968, 969, 970, 971, 972, 974,
976 978, 980, 983, 985, 987, 989, 991, 993, 995,
997, 999, 1002, 1006, 1011, 1015, 1019, 1023, 1028, 1032,
1036 1040, 1045, 1050, 1055, 1059, 1064, 1069, 1076, 1082,
1088 1095, 1101, 1107, 1114, 1120, 1126, 1132, 1141, 1149,
1158 1166, 1173, 1178, 1183, 1188, 1193, 1198, 1203, 1208,
1214 1221, 1227, 1233, 1240, 1246, 1250, 1254, 1259, 1263,
1269 1278, 1286, 1294, 1303, 1309, 1315, 1322, 1328, 1334,
1341 1343, 1345, 1347, 1349, 1351, 1353, 1355, 1357, 1359,
1359 1359, 1359, 1359, 1358, 1356, 1354, 1352, 1350, 1347,
1345 1343, 1341, 1339, 1336, 1334, 1332, 1329, 1327, 1324,
1322 1320, 1317, 1315, 1312, 1307, 1301, 1294, 1288, 1281,
1275, 1270, 1265, 1260, 1256, 1251, 1246, 1240, 1233, 1227,
1221 1214, 1208, 1201, 1194, 1186, 1178, 1170, 1162, 1154,
1148 1144, 1140, 1136, 1131, 1127, 1123, 1118, 1114, 1107,
1099, 1090, 1082, 1074, 1069, 1064, 1058, 1053, 1048, 1043,
1038 1034, 1029, 1025, 1021, 1017, 1013, 1009, 1005, 1001,
997 994, 990, 991, 992, 994, 996, 997, 999, 998,
997, 996, 995, 994, 993, 991, 990, 989, 989, 989
989, 989, 989, 989, 988, 986, 984, 983, 981, 980,
982, 984, 986, 988, 990, 993, 995, 997, 999, 1002,
1005, 1008, 1012};
globale variabelen gebruikt door het programma
unsigned int NumSamples = sizeof(y_data) / 2; aantal elementen in de bovenstaande y_data-]
unsigned int QRSCount = 0; lopende QRS periode msec telling
unsigned int IdleCount = 0; Running inactieve periode msec graaf
unsigned long IdlePeriod = 0; niet-actieve periode wordt afgesteld door pot om in te stellen hartslag
unsigned int staat = INIT; toestanden zijn INIT, QRS, en IDLE
unsigned int DisplayCount = 0; graven 50 msec bijwerken van het 7-segment display
unsigned int tcnt2; Timer2 reload waarde, wereldwijd beschikbaar
float BeatsPerMinute; zwevende punt vertegenwoordiging van de hartslag
unsigned int Bpm; geheel getal versie van de hartslag (tijden 10)
unsigned int BpmLow; laagste hartslag toegestaan (x10)
unsigned int BpmHigh; hoogste hartslag toegestaan (x10)
int waarde; place holder voor analoge ingang 0
unsigned long BpmValues [32] = {0, 0, 0, 0, 0, 0, 0, 0, / / houdt 32 laatste analoge pot lezingen
0, 0, 0, 0, 0, 0, 0, 0, / / voor gebruik in het scherm uitfilteren jitter
0, 0, 0, 0, 0, 0, 0, 0, / / voor gebruik in het scherm uitfilteren jitter
0, 0, 0, 0, 0, 0, 0, 0}; voor gebruik in het scherm uitfilteren jitter
unsigned long BpmAverage = 0; gebruikt in een eenvoudig gemiddelde filter
unsigned char Index = 0; gebruikt in een eenvoudig gemiddelde filter
unsigned int DisplayValue = 0; gefilterde Beats Per Minute verzonden om weer te geven
VOID Setup {}
Configureer de poorten van de output (1 msec intrerrupt indicator en uiterste SPI ondersteuning)
pinMode (9, OUTPUT); 7-segment display chip select (laag naar Selecteer chip)
pinMode (10, OUTPUT); D/A converter chip selecteren (laag naar Selecteer chip)
pinMode (11, OUTPUT); SDI gegevens
pinMode (13, OUTPUT); SCK klok
oorspronkelijke toestand van SPI interface
SPI.begin(); de bus SPI's ochtends.
SPI.setDataMode(0); modus: CPHA = 0, gegevens die zijn vastgelegd op de klok de stijgende rand (laag naar hoog)
SPI.setClockDivider(SPI_CLOCK_DIV64); systeemklok / 64
SPI.setBitOrder(MSBFIRST); bit 7 klokken uit eerste
vaststellen van het bereik van de hartslag toegestaan
BpmLow = 300 (30 HSM x 10)
BpmHigh = (60,0 / (NumSamples * 0.001)) * 10 = (60,0 /.543) * 10 = 1104 (110.49 x 10)
BpmLow = 300;
BpmHigh = (60,0 / ((float) NumSamples * 0.001)) * 10;
De timer overflow interrupt eerst uit te schakelen terwijl wij aan het configureren bent
TIMSK2 & = ~ (1 << TOIE2);
Configureren van timer2 in de normale modus (puur tellen, geen PWM enz.)
TCCR2A & = ~ ((1 << WGM21) | (1 << WGM20));
TCCR2B & = ~ (1 << WGM22);
Selecteer klokbron: interne I/O klok
ASSR & = ~ (1 << AS2);
Uitschakelen vergelijken Match A interrupt inschakelen (alleen willen overloop)
TIMSK2 & = ~ (1 << OCIE2A);
Nu het configureren van de prescaler aan CPU kloksnelheid gedeeld door 128
TCCR2B | = (1 << CS22) | (1 << CS20); Set bits
TCCR2B & = ~ (1 << CS21); Duidelijke bits
We moeten een goede waarde voor het laden van de timer-teller te berekenen.
De volgende laadt de waarde 131 in het Timer 2 teller register
De wiskunde achter dit is:
(CPU-frequentie) / (prescaler waarde) = 125000 Hz = 8us.
(gewenste periode) / 8us = 125.
Max(uint8) + 1-125 = 131;
//
Waarde wereldwijd voor latere reload in ISR opslaan /
tcnt2 = 131;
Tenslotte vracht einde inschakelen de timer
TCNT2 = tcnt2;
TIMSK2 | = (1 << TOIE2);
}
void loop {}
Lees uit de pot van de hartslag (analoge ingang 0)
Waarde = analogRead(0);
kaart van het analoge ingang 0 bereik (0.. 1023) naar het Bpm-bereik (300.. 1104)
BPM = kaart (waarde, 0, 1023, BpmLow, BpmHigh);
Verminderen van de jitter of stuiteren in de minst significante cijfer van het display,
een bewegende gemiddelde filter (32 waarden) zal gladstrijken.
BpmValues [Index ++] = Bpm; nieuwste monster aan acht element array toevoegen
Als (Index == 32) {/ / omgaan met wrap-around
Index = 0;
}
BpmAverage = 0;
for (int i = 0; Ik < 32; i ++) {/ / som van alle waarden in de matrix
BpmAverage += BpmValues [i];
}
BpmAverage >> = 5; Delen door 32 om gemiddelde
nu de 4-cijferige weergave bijwerken - opmaken: XXX. X
Sinds de update is een multi-byte overdracht, interrupts uitschakelen totdat het gedaan
noInterrupts();
DisplayValue = BpmAverage;
interrupts();
gezien de waarde die pot (beats per minute) lezen, het berekenen van de IdlePeriod (msec)
Deze waarde wordt gebruikt door de Timer2 1.0 msec interrupt service routine
BeatsPerMinute = (float) Bpm / 10.0;
noInterrupts();
IdlePeriod (unsigned int) = ((float) 60000.0 / BeatsPerMinute)-(float) NumSamples;
interrupts();
vertraging(20);
}
// ********************************************************************************
Timer2 Interrupt Service Routine
//
Interrupt Service Routine (ISR) om Timer2 overflow op 1.000 msec.
//
//
De Timer2 interrupt-functie wordt gebruikt voor het verzenden van het 16-bits golfvorm punt
voor de Microchip MCP4921 uiterste converter SPI interface te gebruiken.
//
De Timer2 interrupt functie wordt ook gebruikt voor het verzenden van de actuele hartslag
als van de potentiometer elke 50 Timer2 interrupts naar gelezen het 7-segment display.
//
De pot wordt gelezen en de hartslag wordt berekend in de lus van de achtergrond.
Door het uitvoeren van zowel SPI randapparatuur op interruptniveau, wij "serialize" hen en voorkomen
corruptie door een SPI transmissie wordt onderbroken door de andere.
//
Een machime staat is geïmplementeerd om dit te bereiken. De Staten zijn:
//
INIT - in principe worden gewist van de tellers en stelt u de status voor de QRS.
//
QRS - uitgangen het volgende ECG golfvorm gegevenspunt elke 1.0 msec
Er zijn 543 van deze QRS complex gegevenspunten.
//
IDLE - variabele periode na het QRS deel.
D/A houdt eerste ECG waarde (939) voor elk van de niet-actieve periode.
Niet-actieve periode varieert zodat de aanpassing van de basisrente van hart;
een waarde van nul msec voor de niet-actieve periode geeft 110.4 slagen per min
terwijl de niet-actieve periode van ten hoogste 457 msec geeft 30,0 bpm.
//
Merk op dat de niet-actieve periode wordt berekend op de belangrijkste achtergrond
lus door het lezen van een pot en haar bereik converteren naar een geschikt
voor de periode van de achtergrond. De interrupt routine leest dit
waarde om te bepalen wanneer om te stoppen met de niet-actieve periode.
//
De overdracht van de volgende gegevenspunt naar de D/A-omzetter via SPI neemt
ongeveer 63 microseconden (dat omvat twee SPI byte overbrengingen).
//
De transmissie van de hartslag cijfers naar het Sparkfun 7-segment display
duurt ongeveer 350 usec (het is alleen verzonden elke 50 Timer2 interrupts)
//
// ********************************************************************************
ISR(TIMER2_OVF_vect) {}
Laden van de timer
TCNT2 = tcnt2;
statusmachine
schakelaar (State) {}
Case INIT:
nul de QRS- en niet-actieve items
QRSCount = 0;
IdleCount = 0;
DisplayCount = 0;
volgende staat ingesteld op QRS
Staat = QRS;
breken;
Case QRS:
uitgang van het volgende monster in de golfvorm van de QRS naar de D/A converter
DTOA_Send(y_data[QRSCount]);
voorschot monster teller en controleren op einde
QRSCount ++;
Als (QRSCount > = NumSamples) {}
IDLE startperiode en output te DTOA eerste steekproef
QRSCount = 0;
DTOA_Send(y_data[0]);
Staat = STATIONAIR;
}
breken;
Case IDLE:
Aangezien de D/A-omzetter de vorige waarde geschreven bewaard blijft, moeten we alle
om te doen is bepalen hoe lang de inactieve periode moet zijn.
voorschot inactief teller en controleren op einde
IdleCount ++;
de IdlePeriod wordt berekend in de hoofdlus (uit een pot)
Als (IdleCount > = IdlePeriod) {}
IdleCount = 0;
Staat = QRS;
}
breken;
standaard:
breken;
}
uitvoer naar het 7-segment display elke 50 msec
DisplayCount ++;
Als (DisplayCount > = 50) {}
DisplayCount = 0;
Display7Seg_Send(DisplayValue);
}
}
// ***************************************************************************************************
VOID DTOA_Send(unsigned short)
//
Doel: 12-bit D/A waarde verzenden naar Microchip MCP4921 uiterste converter (0.. 4096)
//
//
Input: DtoAValue - 12-bit D/A-waarde (0.. 4096)
//
//
De DtoAValue is voor de functienaam geplaatst met de A / B, BUF, GA, en SHDN bits voorafgaand aan de verzending.
//
SCHRIJVEN VAN DE OPDRACHT
// |-----------|-----------|-----------|-------------|--------------------------------------------------------------------------------|
// | A/B | BUF | GA | SHDN | D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 |
// | | | | | |
|setting: |setting: |setting: | Instellen: | DtoAValue (12-bits) |
// | 0 | 0 | 1 | 1 | |
// | DAC-A |unbuffer | 1 x |power-on| (0.. 4096 zal output als 0 volt.. 5 volt) |
// |-----------|------------|----------|-------------|--------------------------------------------------------------------------------|
// 15 14 13 12 11 0
// To D/A <======================================================================================
//
Opmerking: WriteCommand is geklokt uit met beetje 15 eerst!
//
//
Rendement: niets
//
//
I/O middelen: Digital Pin 9 = chip Selecteer (laag te selecteren van chip)
Digitale Pin 13 = SPI kloksnelheid
Digitale Pin 11 = SPI Data
//
Opmerking: door de LDAC-pin aarding in de hardware hook-up, de SPI-gegevens zal worden klokte in de
De hendels van de D/A-omzetter wanneer de chip select op de einde-van-transfer stijgt.
//
Deze routine neemt 63 usec met behulp van een Adafruit Menta
// ***************************************************************************************************
VOID DTOA_Send (unsigned short DtoAValue) {}
byte gegevens = 0;
Selecteer de D/A-chip (laag)
digitalWrite (10, 0); chip Selecteer laag
verzenden van de eerste 0011xxxx van de hoge byte
Gegevens = highByte(DtoAValue);
Gegevens = 0b00001111 & gegevens;
Gegevens = 0b00110000 | Gegevens;
SPI.transfer(Data);
Stuur de lage byte volgende xxxxxxxx
Gegevens = lowByte(DtoAValue);
SPI.transfer(Data);
alles gedaan, u deselecteert de chip (dit werkt de D/A met de nieuwe waarde)
digitalWrite (10, 1); chip Selecteer hoog
}
// ***************************************************************************************************
VOID Display7Seg_Send(char *)
//
Doel: 4 cijfers verzenden in SparkFun Serial 7-Segment Display (vereist 4 SPI schrijft)
//
Ingang: waarde - unsigned int versie van BeatsPerMinute
//
Rendement: niets
//
I/O middelen: Digital Pin 10 = chip Selecteer (laag te selecteren van chip)
Digitale Pin 13 = SPI kloksnelheid
Digitale Pin 11 = SPI Data
//
Opmerking: deze routine neemt 350 usec met behulp van een Adafruit Menta
// ***************************************************************************************************
VOID Display7Seg_Send (unsigned int tekeer) {}
uint8_t digit1, digit2, digit3, digit4;
unsigned int-waarde;
omzetten in vier cijfers (set toonaangevende nullen naar spaties, 0x78 is het leeg teken)
waarde = hartslag;
digit1 = waarde / 1000;
waarde-= digit1 * 1000;
Als (digit1 == 0) digit1 = 0x78;
digit2 = waarde / 100;
waarde-= digit2 * 100;
Als ((digit1 == 0x78) & & (digit2 == 0)) digit2 = 0x78;
digit3 = waarde / 10;
waarde-= digit3 * 10;
Als ((digit1 == 0x78) & & (digit2 == 0x78) & & (digit3 == 0)) digit3 = 0x78;
Digit4 = waarde;
digitalWrite (9, laag); Selecteer de weergave van de 7-seg Sparkfun
SPI.transfer(0x76); Reset display
SPI.transfer(0x7A); helderheid opdracht
SPI.transfer(0x00); 0 = helder, 255 = dim
SPI.transfer(digit1); Duizenden Digit
SPI.transfer(digit2); Honderden cijfers
SPI.transfer(digit3); Tientallen Digit
SPI.transfer(digit4); Die cijfers
SPI.transfer(0x77); decimaaltekens opdracht instellen
SPI.transfer(0x04); inschakelen van dec pt tussen de cijfers 3 en 4
digitalWrite (9, hoge); vrijgeven van Sparkfun 7-seg weergeven
}