EKG Simulator (24 / 27 steg)
Steg 24: EKG Simulator - Arduino skiss
// ***************************************************************************************************EKG SIMULATOR
//
Syfte: simulera normal sinus rytm ECG signalen (3-blytak RA, LA RL)
//
Bakgrund:
//
I normala EKG (ECG eller EKG om du är tysk), tre leder utgör den
Einthovens triangel. Två leder är tejpade på höger och vänster sida av bröstet ovan
hjärtat (RA = höger arm, LA = vänster arm) och en bly är tejpade på lägre bröstet, vanligtvis
på höger höft (RL = höger ben).
//
Det är viktigt att veta att dessa EKG-signalen är millivolt i amplitud. Detta kan uppnås genom
utfodring av D/A omvandlaren genom en spänningsavdelare att komma till de millivolt nivåerna.
//
//
EKG-signalen:
//
Jag hittade en lämplig EKG-kurva från internet. Här är hur jag konverterade en bild från mitt
bildskärm till en C språk array av A/D värden, varje fördelade 1,00 MS apart.
//
A. skärmdump av vågformen med fri skärmen capture program MWSNAP
http://www.mirekw.com/winfreeware/MWSnap.html
//
B. digitalisera jpeg vågformen med programmet gratis digitalisering ENGAUGE
http://digitizer.sourceforge.net/
//
//
C: Jag skrev en Python program för att konvertera de tämligen oregelbundet proverna från ENGAUGE
till en matris med värden fördelade 1.0 millisekunder isär med linjär interpolation.
Sedan skapade jag en textfil där dessa datapunkter var del av en C språk array
konstruera; datapunkterna är C initieringar.
//
D: klipp-och-klistra från text fil C data matrisen med initieringar i den
Arduino skiss nedan.
//
//
Arduino resurser:
//
Digital utgång # 9 - chip välja 7-segment display SPI port (låg att välja)
Digital utgång # 10 - chip Välj för D/A-omvandlare (lågt att välja)
Digital utgång # 11 - SDI data till D/A-omvandlare (SPI gränssnitt)
Digital utgång # 13 - SCK klocka till D/A-omvandlare (SPI gränssnitt)
//
Analog Input # 0 - torkar mittstift för 5 k ohm krukan (hjärtfrekvens justera)
//
Jag följde den Timer2 setup som beskrivs av Sebastian Wallin
http://popdevelop.com/2010/04/mastering-timer-interrupts-on-the-Arduino/
//
Jag har ställt in den SPI gränssnittet enligt australiska John Boxall, utmärkt anvisningar
vars underbar hemsida har många utmärkta Arduino tutorials:
http://tronixstuff.wordpress.com/
//
Programmerare: James P Lynch
lynch007
//
// ***************************************************************************************************
#include "SPI.h" / / stöder SPI gränssnitt till D/A-omvandlare och 7-segment display
#include < Wire.h > / / behöver tråd biblioteket
olika konstanter som används av vågformen generator
#define INIT 0
#define IDLE 1
#define QRS 2
#define fyra 4
#define tre 3
#define två 2
#define en 1
// *******************************************************************************************
y_data [543] - digitaliserade EKG-kurva, på 1,0 MSEK
//
Vågform skalas för en 12-bitars D/A omvandlare (0.. 4096)
//
En 60 beat/min ECG skulle kräva denna vågform (543 prover) plus 457 prover
för det första y_data [0] värdet på 939.
//
// *********************************************************************************************
CONST kort 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 998, 996, 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};
globala variabler används av programmet
unsigned int NumSamples = sizeof(y_data) / 2; antalet element i y_data [] ovan
unsigned int QRSCount = 0; rinnande QRS period msec greve
unsigned int IdleCount = 0; igång inaktiv period msec greve
osignerade långa IdlePeriod = 0; inaktiv period justeras av potten för att ange puls
unsigned int State = INIT; staterna är INIT, QRS och IDLE
unsigned int DisplayCount = 0; räkningarna 50 MSEK till uppdatera den 7-segment displayen
unsigned int tcnt2; Timer2 reload, globalt tillgängligt värde
float BeatsPerMinute; flytande punkt representation av pulsen
unsigned int Bpm; heltal version av hjärtfrekvens (gånger 10)
unsigned int BpmLow; lägsta hjärtfrekvens tillåtna (x10)
unsigned int BpmHigh; högsta puls tillåtna (x10)
int värde; för analog ingång 0
osignerade långa BpmValues [32] = {0, 0, 0, 0, 0, 0, 0, 0, / / rymmer 32 sista analoga potten avläsningar
0, 0, 0, 0, 0, 0, 0, 0, / / för användning i filtrera bort display jitter
0, 0, 0, 0, 0, 0, 0, 0, / / för användning i filtrera bort display jitter
0, 0, 0, 0, 0, 0, 0, 0}; för användning i filtrera bort display jitter
osignerade långa BpmAverage = 0; används i ett enkelt genomsnitt filter
unsigned char Index = 0; används i ett enkelt genomsnitt filter
unsigned int DisplayValue = 0; filtrerade Beats Per Minute skickade att Visa
void setup() {
Konfigurera utgående portar (1 msec intrerrupt indikator och D/A SPI stöd)
pinMode (9, OUTPUT); 7-segment display chip Välj (lågt att välja chip)
pinMode (10, OUTPUT); D/A-omvandlare chip Välj (lågt att välja chip)
pinMode (11, OUTPUT); SDI data
pinMode (13, OUTPUT); SCK klocka
Starttillståndet för SPI gränssnitt
SPI.begin(); vakna upp SPI bussen.
SPI.setDataMode(0); läge: CPHA = 0, data fångas på klockans stigande kanten (låg till hög)
SPI.setClockDivider(SPI_CLOCK_DIV64); systemklockan / 64
SPI.setBitOrder(MSBFIRST); bit 7 klockor ut först
upprätta puls spänna tillåtet
BpmLow = 300 (30 bpm 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;
Först inaktivera timern overflow avbrottet medan vi konfigurerar
TIMSK2 & = ~ (1 << TOIE2);
Konfigurera timer2 i normalt läge (ren räkna, ingen PWM etc.)
TCCR2A & = ~ ((1 << WGM21) | (1 << WGM20));
TCCR2B & = ~ (1 << WGM22);
Välj klocka Källa: inre I/O klocka
ASSR & = ~ (1 << AS2);
Inaktivera jämför Match A interrupt enable (endast vill overflow)
TIMSK2 & = ~ (1 << OCIE2A);
Nu konfigurera prescaler till CPU klocka dividerat med 128
TCCR2B | = (1 << CS22) | (1 << CS20); Ange bitar
TCCR2B & = ~ (1 << CS21); Tydlig bit
Vi måste beräkna ett korrekt värde för att ladda räknaren timer.
Följande läses värdet 131 in Timer 2 counter registret
Matten bakom detta är:
(CPU frekvens) / (prescaler värde) = 125000 Hz = 8us.
(önskad period) / 8us = 125.
Max(uint8) + 1-125 = 131;
//
Spara värdet globalt för senare reload i ISR /
tcnt2 = 131;
Slutligen ladda slutet aktivera timern
TCNT2 = tcnt2;
TIMSK2 | = (1 << TOIE2);
}
void loop() {
läsa från puls potten (Analog insignal 0)
Värde = analogRead(0);
Mappa Analog ingång 0 spänna (0.. 1023) till intervallet Bpm (300.. 1104)
BPM = karta (värde, 0, 1023, BpmLow, BpmHigh);
Att minska jitter eller studsa i bildskärmens minst signifikanta siffra,
ett glidande medelvärde filter (32 värden) kommer att utjämna det.
BpmValues [Index ++] = Bpm; lägga till senaste provet i en matris med åtta element
om (Index == 32) {/ / handtag omlott
Index = 0;
}
BpmAverage = 0;
för (int jag = 0; jag < 32; i ++) {/ / summering av alla värden i vektorn
BpmAverage += BpmValues [i];
}
BpmAverage >> = 5; Dividera med 32 att få genomsnittliga
nu uppdatera 4-siffrig display - format: XXX. X
eftersom uppdateringen är en multi-byte överföring, inaktivera avbrott tills det är gjort
noInterrupts();
DisplayValue = BpmAverage;
interrupts();
givet den potten (slag per minut) läste i, beräkna IdlePeriod (MS)
Detta värde används av Timer2 1,0 MSEK avbrottstjänstens rutin
BeatsPerMinute = (float) Bpm / 10,0;
noInterrupts();
IdlePeriod = (unsigned int) ((float) 60000.0 / BeatsPerMinute)-(float) NumSamples;
interrupts();
Delay(20);
}
// ********************************************************************************
Timer2 avbrottstjänstens rutin
//
Avbryta tjänsten rutin (ISR) för Timer2 spill på 1.000 msec.
//
//
Timer2 avbryta funktionen används för att skicka 16-bitars vågform punkt
till mikrochip MCP4921 D/A Omformaren i SPI-gränssnittet.
//
Funktionen Timer2 avbrott också används för att skicka aktuell puls
som läst från potentiometern varje 50 Timer2 avbryter 7-segment display.
//
Potten är Läs och pulsen beräknas i bakgrunden slingan.
Genom att köra båda SPI kringutrustning på avbrottsnivå, vi "serialize" dem och undvika
korruption av en SPI överföringen avbryts av andra.
//
En stat machtal implementeras för att åstadkomma detta. Det är staterna är:
//
INIT - i princip rensar räknarna och ställer staten QRS.
//
QRS - utgångar vågform nästa EKG-data pekar varje 1,0 MSEK
Det finns 543 dessa QRS-komplex datapunkter.
//
IDLE - variabel period efter QRS delen.
D/A håller första ECG värde (939) för alla den inaktiva perioden.
Inaktiv period varierar så att justering av den grundläggande pulsen;
ett värde av noll msec för inaktiv period ger 110,4 slag per min
medan den inaktiva maximiperioden av 457 MSEK ger 30,0 bpm.
//
Observera att den inaktiva perioden beräknas i den huvudsakliga bakgrunden
loopa genom att läsa en pott och omvandla sitt utbud till en lämplig
för perioden bakgrund. Avbryta rutin läser detta
värde för att bestämma när du ska sluta den inaktiva perioden.
//
Överföringen av den nästa datapunkten till D/A omvandlaren via SPI äger
cirka 63 mikrosekunder (som inkluderar två SPI byte överföringar).
//
Överföring av puls siffror till Sparkfun 7-segment display
tar cirka 350 usec (det är bara sänds varje 50 Timer2 avbrott)
//
// ********************************************************************************
ISR(TIMER2_OVF_vect) {
Ladda om timern
TCNT2 = tcnt2;
tillståndsdator
Växla (staten) {
fall INIT:
noll QRS och inaktiv räknare
QRSCount = 0;
IdleCount = 0;
DisplayCount = 0;
Ange nästa QRS
State = QRS;
bryta;
mål QRS:
utgång nästa provet i QRS vågformen till D/A omvandlare
DTOA_Send(y_data[QRSCount]);
Advance prov counter och kontrollera om slutet
QRSCount ++;
om (QRSCount > = NumSamples) {
Starta inaktiv period och utgång första provet till DTOA
QRSCount = 0;
DTOA_Send(y_data[0]);
State = inaktiv;
}
bryta;
fall IDLE:
eftersom D/A-omvandlare kommer att hålla det föregående värdet skrivet, har alla vi
att göra är att bestämma hur lång den inaktiva perioden bör vara.
Advance inaktiv counter och kontrollera om slutet
IdleCount ++;
IdlePeriod beräknas i det viktigaste kretsar (från en pott)
om (IdleCount > = IdlePeriod) {
IdleCount = 0;
State = QRS;
}
bryta;
standard:
bryta;
}
utgång till 7-segment visas varje 50 MSEK
DisplayCount ++;
om (DisplayCount > = 50) {
DisplayCount = 0;
Display7Seg_Send(DisplayValue);
}
}
// ***************************************************************************************************
void DTOA_Send(unsigned short)
//
Syfte: skicka 12-bitars D/A värde till mikrochip MCP4921 D/A converter (0.. 4096)
//
//
Ingång: DtoAValue - 12-bitars D/A värde (0.. 4096)
//
//
DtoAValue är föregås med A / B, BUF, GA, och SHDN bitar före överföringen.
//
SKRIV KOMMANDOT
// |-----------|-----------|-----------|-------------|--------------------------------------------------------------------------------|
// | A / B | BUF | GA | SHDN | D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01 D00 |
// | | | | | |
|Setting: |setting: |setting: | Inställning: | DtoAValue (12 bitar) |
// | 0 | 0 | 1 | 1 | |
// | DAC-A |unbuffer | 1 x Upplev-on| (0.. 4096 kommer ut som 0 volt.. 5 volt) |
// |-----------|------------|----------|-------------|--------------------------------------------------------------------------------|
15 14 13 12 11 0
// To D/A <======================================================================================
//
Obs: WriteCommand är klockat med lite 15 först!
//
//
Avkastning: ingenting
//
//
I/O resurser: Digital stift 9 = chip Välj (låg att välja chip)
Digital stift 13 = SPI klocka
Digital stift 11 = SPI Data
//
Obs: av jordning LDAC * PIN-koden i hårdvara krok-up, SPI data kommer klockas in i den
D/A-omvandlare spärrarna när chip Välj stiger på slutet-av-överföring.
//
Denna rutin tar 63 usec med hjälp av en Adafruit Menta
// ***************************************************************************************************
void DTOA_Send (osignerad kort DtoAValue) {
byte Data = 0;
Välj D/A chip (låg)
digitalWrite (10, 0); chip Välj låg
Skicka den övre byte första 0011xxxx
Data = highByte(DtoAValue);
Data = 0b00001111 & Data.
Data = 0b00110000 | Uppgifter.
SPI.transfer(Data);
Skicka låg byte nästa xxxxxxxx
Data = lowByte(DtoAValue);
SPI.transfer(Data);
allt gjort, avmarkera chip (detta uppdaterar D/A med det nya värdet)
digitalWrite (10, 1); chip Välj hög
}
// ***************************************************************************************************
void Display7Seg_Send(char *)
//
Syfte: skicka 4 siffror till SparkFun följetong 7-Segment Display (kräver 4 SPI skriver)
//
Ingång: värde - unsigned int version av BeatsPerMinute
//
Avkastning: ingenting
//
I/O resurser: Digital stift 10 = chip Välj (låg att välja chip)
Digital stift 13 = SPI klocka
Digital stift 11 = SPI Data
//
Obs: denna rutin tar 350 usec med hjälp av en Adafruit Menta
// ***************************************************************************************************
void Display7Seg_Send (unsigned int hjärtrytm) {
uint8_t digit1, digit2, digit3, digit4;
unsigned int värde;
konvertera till fyra siffror (set ledande nollor till blankvärden; 0x78 är det tomt tecknet)
värde = hjärtrytm;
digit1 = värde / 1000;
värdet-= digit1 * 1000;
om (digit1 == 0) digit1 = 0x78;
digit2 = värde / 100;
värdet-= digit2 * 100;
om ((digit1 == 0x78) & & (digit2 == 0)) digit2 = 0x78;
digit3 = värde / 10;
värdet-= digit3 * 10;
om ((digit1 == 0x78) & & (digit2 == 0x78) & & (digit3 == 0)) digit3 = 0x78;
digit4 = värde;
digitalWrite (9, låg). Markera Visa Sparkfun 7-seg
SPI.transfer(0x76); Återställ visningen
SPI.transfer(0x7A); ljusstyrka-kommandot
SPI.transfer(0x00); 0 = ljusa, 255 = dim
SPI.transfer(digit1); Tusentals siffra
SPI.transfer(digit2); Hundratals siffra
SPI.transfer(digit3); Tens siffra
SPI.transfer(digit4); Sådana siffror
SPI.transfer(0x77); Ange decimaler kommando
SPI.transfer(0x04); slå på dec pt mellan siffror 3 och 4
digitalWrite (9, hög). release Sparkfun 7-seg display
}