Fórum témák

» Több friss téma
Fórum » Arduino
A klónok CH340 Soros-USB illesztőjének drivere (Letöltés)
Lapozás: OK   545 / 838
(#) Rober_4 válasza Rober_4 hozzászólására (») Júl 19, 2019 /
 
Szerintetek az Arduino Due Dac-ja signed, vagy unsigned adatokat vár?
Itt azt írják, ha jól olvastam, hogy a dolog eszközspecifikus:
pcm
(#) sector99 válasza sector99 hozzászólására (») Júl 20, 2019 /
 
Sziasztok !
Most lett időm foglalkozni az óraforgatóval. Első "önálló" programom, így néz ki:

  1. /*Forgató karórához Attiny85 - Digispark
  2.  * 2019. július
  3.  * 1 és 2 pin bemenet, 3 és 4 pin  kimenet
  4.  * H híd meghajtásához
  5.  */
  6.  
  7. int  switchOnePin = 1;    // digital  bemenetek 1 és 2 pin
  8. int  switchTwoPin = 2;    //
  9.  
  10. bool b1;
  11. bool b2;
  12.  
  13. int  outOnePin = 3;       // kimenet híd meghajtáshoz, 3 és 4 pin
  14. int  outTwoPin = 4;
  15. int currentstate = 0;
  16. int newState = 0;
  17. int oldState = 0;
  18.  
  19. void setup() {
  20.  
  21.   pinMode(switchOnePin, INPUT);
  22.   pinMode(switchTwoPin, INPUT);
  23.   pinMode( outOnePin, OUTPUT);
  24.   pinMode( outTwoPin, OUTPUT);
  25.   digitalWrite (outOnePin, LOW);
  26.   digitalWrite (outTwoPin, LOW);  
  27.   // delay (1000);
  28.  
  29. }
  30.  
  31. void loop()
  32. {
  33.  
  34.   checkButtons();
  35.   delay(5000);
  36.  
  37.  
  38.   if (oldState != newState)
  39.   {
  40.  
  41.     if (newState == 1 )
  42.     {
  43.       jobbra();
  44.     }
  45.  
  46.     if (newState == 2 )
  47.     {
  48.       balra();
  49.     }
  50.  
  51.     if (newState == 3 )
  52.     {
  53.       valtva();
  54.     }
  55.   }
  56. }
  57.  
  58. void checkButtons()
  59. {
  60.   int b1 = digitalRead(switchOnePin);
  61.   int b2 = digitalRead(switchTwoPin);
  62.   delay(1000);
  63.  
  64.   if (b1 == LOW)
  65.   {
  66.     newState = 1;
  67.   }
  68.  
  69.   if (b2 == LOW)
  70.   {
  71.     newState = 2;
  72.   }
  73.  
  74.   if (b1 == LOW & b2 == LOW)
  75.   {
  76.     newState = 3;
  77.   }
  78.  
  79. }
  80.  
  81. void jobbra()
  82. {
  83.   digitalWrite (outTwoPin, LOW);
  84.   delay(100);
  85.   digitalWrite (outOnePin, HIGH);
  86.   oldState = newState;
  87. }
  88.  
  89. void balra()
  90. {
  91.   digitalWrite (outOnePin, LOW);
  92.   delay(100);
  93.   digitalWrite (outTwoPin, HIGH);
  94.   oldState = newState;
  95. }
  96.  
  97. void valtva()
  98. {
  99.  
  100.   digitalWrite (outOnePin, LOW);
  101.   delay(100);
  102.   digitalWrite (outTwoPin, HIGH);
  103.   delay(10000);
  104.   digitalWrite (outTwoPin, LOW);
  105.   delay(10000);
  106.   digitalWrite (outOnePin, HIGH);
  107.  
  108. }


A dobozban kapcsoló van, nem nyomógomb. 3 állás: jobbra, balra és váltva forgás a cél. Biztosan nem fogja öt percenként kapcsolgatni. Lehet, hogy van más - szebb, egyszerűbb - megoldás is, nekem elsőre ez sikerült. Kipróbálva még nem volt. És még így a folyamatos váltva forgás nincs megoldva, hogy addig csinálja, amíg a kapcsoló állása nem változik.
Várom véleményeket - biztos van benne hiba. Köszi !
Hogy kell kódot beilleszteni, hogy ne legyen ennyi üres sor benne ?
(#) kapu48 válasza sector99 hozzászólására (») Júl 20, 2019 /
 
Helyesen Logical AND:
Bővebben: Link

Így b1 = LOW és b2 = LOW esetén mind a három feltétel vizsgálatod teljesül.
Jobb lenne így:
  1. //newState = 0;
  2. if (b1 == LOW && b2 == HIGH)
  3. {
  4.   newState = 1;
  5. } else if (b1 == HIGH && b2 == LOW)
  6. {
  7.    newState = 2;
  8. }else  if (b1 == LOW && b2 == LOW)
  9. {
  10.    newState = 3;
  11. } // else newState = 0; // Valahogyan ki is kellene kapcsolni?
A hozzászólás módosítva: Júl 20, 2019
(#) sector99 válasza kapu48 hozzászólására (») Júl 20, 2019 /
 
Köszi a módosítást ! Az igazság az, hogy így a nyugdíjazás küszöbén már nem úgy működik az agy mint 20-30 évesen...
Igen, ez csak teljesen alap program, annyit akarok még bele tenni, hogy 2-3 órányi működés után álljon le a forgatás. Ezt hogy lenne legegyszerűbb ? Számolni a loop ciklust vagy az időt mérni ?
(#) kapu48 válasza sector99 hozzászólására (») Júl 20, 2019 / 1
 
hour to milliseconds: 3h = 10800000ms
Bővebben: Link

  1. unsigned long time;
  2.  
  3. setupban:
  4. time = millis() + 10800000;
  5.  
  6. loopban:
  7. if(millis() > time) newState = 0;
(#) sector99 válasza kapu48 hozzászólására (») Júl 20, 2019 /
 
Köszi!
(#) asrock hozzászólása Júl 23, 2019 /
 
Minden 1 min bekapcsol 1 secre ez a fügvény!

  1. if ( now.minute() >= 1 && now.second() < 1)


itt nem veszi figyelembe az ora váltózót?

  1. if (now.hour() >= 5 && now.minute() >= 1 && now.second() < 1)


Minden 5 oránban 1sec igaz feltételt szeretnék!
(#) tbarath válasza asrock hozzászólására (») Júl 23, 2019 /
 
Egyrészt a "Minden 1 min bekapcsol 1 secre ez a fügvény!" mondat konkrétan értelmetlen, és nem is egyértelmű.
Az 1. sorod lefut
- minden óra
- 1 és 59 perc között (0 perckor nem fut le)
- 0 másodperckor.

A 2. sorod lefut:
- 5 és 23 óra között
- 1 és 59 perc között (0 perckor nem fut le)
- 0 másodperckor.

Ha minden 5. órában akarod lefuttatni, akkor vagy a modulus operátor lesz a barátod, vagy megírsz 4-5 db. órás feltételt vaggyal

  1. if ( (0 == h % 5) && (0 == m) && (0 == s) )
  2.  
  3. //vagy
  4.  
  5. if ( ( (0 == h) || (5 == h) || (10 == h) || (15 == h) || (20 == h) )
  6.                 && (0 == m) && (0 == s) )


De ez csak akkor működik, ha minimális a futó kód, minden egyes másodpercben "jár is erre" a cpu, lefut a kód másodpercenként legalább 1x, ami messze nem triviális. Ha garantáltak akarsz (kb.) 5 óránként 1 másodpercig valamit csinálni, akkor azt máshogy célszerű csinálni.
(#) asrock válasza tbarath hozzászólására (») Júl 23, 2019 /
 
rtc modult frissítenék ntp röl 5 óránkét 1sec elég lenne igaz feltételnek erre a megoldásra if függvénnyel..
Amit írtál ezzel megoldható 5 óránként 1sec igaz if feltétel?
(#) tbarath válasza asrock hozzászólására (») Júl 23, 2019 /
 
Én inkább úgy csinálnám, hogy minden frissítéskor letárolnám a következő _órát_, amikor frissíteni akarok, és azt vizsgálnám, hogy tartunk-e már ott. Ha igen, akkor frissítenék

  1. uint_8t nextRefreshHour = -1;
  2.  
  3. //....
  4. if (nextRefreshHour <= now.hour()){
  5.         // itt frissítem az időt NTP-ről
  6.         nextRefreshHour = (nextRefreshHour +5) % 24;
  7. }
  8. //....


A kód egyébként nincs tesztelve, és az se biztos, hogy fordul.
(#) asrock válasza tbarath hozzászólására (») Júl 23, 2019 /
 
Tesztelem is...
Köszönöm!
(#) asrock válasza tbarath hozzászólására (») Júl 23, 2019 /
 
uint_8t arduino ide fordító nem ismeri fel még #include <stdint.h> sem mivel tudom helyettesíteni ezt a váltózót?
(#) pipi válasza asrock hozzászólására (») Júl 23, 2019 /
 
Hali!
unsigned char ugyanez szerintem...
(#) vargham válasza asrock hozzászólására (») Júl 23, 2019 /
 
Az uint8_t lesz.
(#) asrock válasza tbarath hozzászólására (») Júl 23, 2019 /
 
Egyszerű ledel monitorozom a cselekményt de nem cselekszik levetem a változót second ra...
5sec kelene a lednek állapotot váltani ?

nextRefreshHour = -1 nem ad értéket soros monitorozáskor!


  1. #include <Wire.h>
  2. #include "RTClib.h"
  3. #include <stdint.h>
  4.  
  5. int led=D3;
  6. int button= D4;
  7. RTC_DS3231 rtc;
  8.  
  9. void setup () {
  10.   pinMode(led, OUTPUT);
  11.   pinMode(button,INPUT_PULLUP);
  12.   pinMode(D1,INPUT_PULLUP);  
  13. pinMode(D2,INPUT_PULLUP);
  14.  Serial.begin(115200);
  15.  Wire.begin(D2,D1);
  16.   if (! rtc.begin()) {
  17.     Serial.println("Couldn't find RTC");
  18.    
  19.   }
  20.  
  21.   if (rtc.lostPower()) {
  22.     Serial.println("RTC is NOT running!");
  23.     // following line sets the RTC to the date & time this sketch was compiled
  24.      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  25.     // This line sets the RTC with an explicit date & time, for example to set
  26.     // January 21, 2014 at 3am you would call:
  27.     delay(1000);
  28.      //rtc.adjust(DateTime(2014, 1, 1, 1, 0, 0));
  29.   }
  30. }
  31.  
  32. void loop () {
  33.  
  34.   DateTime now = rtc.now();
  35.  
  36.  
  37.  */    
  38. Serial.print(now.hour(), DEC);
  39.     Serial.print(':');
  40.     Serial.print(now.minute(), DEC);
  41.     Serial.print(':');
  42.     Serial.print(now.second(), DEC);
  43.     Serial.println();
  44.      
  45.    
  46. //delay(1000);
  47.  
  48.  
  49. uint8_t nextRefreshHour = -1;
  50.  
  51. if (nextRefreshHour <= now.second()){
  52.         // itt frissítem az időt NTP-ről
  53.         nextRefreshHour = (nextRefreshHour +5) % 60;
  54.       digitalWrite(led,HIGH);  
  55.  
  56.     Serial.print(nextRefreshHour);
  57.     Serial.println();
  58. }
  59. else
  60. {
  61.  
  62.   digitalWrite(led,LOW);
  63.   }
  64.  
  65.  
  66.     }
(#) kapu48 válasza asrock hozzászólására (») Júl 23, 2019 /
 
Meghatározás uint8_t:
Bővebben: Link
#define uint8_t unsigned char // The unsigned char datatype encodes numbers from 0 to 255.
Vagy:
typedef unsigned char uint8_t

Ennek nem adhatsz -1 értéket!

Legalább az alapfogalmakkal tisztában kellene lenni!
A hozzászólás módosítva: Júl 23, 2019
(#) asrock válasza kapu48 hozzászólására (») Júl 23, 2019 /
 
Csak követem amit itt irtok!
(#) tbarath válasza kapu48 hozzászólására (») Júl 24, 2019 /
 
Bocsánat, ezt én csesztem el.
Ha -1 értéket akarok adni, akkor nem jó az unsigned típus, signed kell. És akarok -1 értéket adni, mert az óra 0-23 közötti lehet, és azt akarom, hogy először mindenképp lefusson.

Legyen a változó típusa int, és ne a loop-on belül legyen deklarálva, hanem a loop() előtt!
(#) asrock válasza tbarath hozzászólására (») Júl 24, 2019 /
 
Van olyan idö egy percben amikor elsö 15sec nem irja nextRefreshHour változót ez mitöl van?
  1. #include <Wire.h>
  2. #include "RTClib.h"
  3. #include <stdint.h>
  4.  
  5. int led=D3;
  6. int button= D4;
  7. RTC_DS3231 rtc;
  8. int nextRefreshHour = 0;
  9. void setup () {
  10.   pinMode(led, OUTPUT);
  11.   pinMode(button,INPUT_PULLUP);
  12.   pinMode(D1,INPUT_PULLUP);
  13. pinMode(D2,INPUT_PULLUP);
  14.  Serial.begin(115200);
  15.  Wire.begin(D2,D1);
  16.   if (! rtc.begin()) {
  17.     Serial.println("Couldn't find RTC");
  18.    
  19.   }
  20.  
  21.   if (rtc.lostPower()) {
  22.     Serial.println("RTC is NOT running!");
  23.     // following line sets the RTC to the date & time this sketch was compiled
  24.      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  25.     // This line sets the RTC with an explicit date & time, for example to set
  26.     // January 21, 2014 at 3am you would call:
  27.     delay(1000);
  28.      //rtc.adjust(DateTime(2014, 1, 1, 1, 0, 0));
  29.   }
  30. }
  31.  
  32. void loop () {
  33.  
  34.   DateTime now = rtc.now();
  35.  
  36.  
  37.  
  38. Serial.print(now.hour(), DEC);
  39.     Serial.print(':');
  40.     Serial.print(now.minute(), DEC);
  41.     Serial.print(':');
  42.     Serial.print(now.second(), DEC);
  43.     Serial.println();
  44.      
  45.    
  46. delay(1000);
  47.  
  48.  
  49.  
  50.  
  51. if (nextRefreshHour <= now.second()){
  52.        
  53.         nextRefreshHour = (nextRefreshHour +5) % 60;
  54.       //  nextRefreshHour = (nextRefreshHour +5) / 60;
  55.       digitalWrite(led,HIGH);
  56.  
  57.     Serial.print(nextRefreshHour);
  58.     Serial.println();
  59. }
  60. else
  61. {
  62.  
  63.   digitalWrite(led,LOW);
  64.   }
  65.  
  66.  
  67.     }


soros port:
  1. 14:16:28
  2. 5
  3. 14:16:29
  4. 10
  5. 14:16:30
  6. 15
  7. 14:16:31
  8. 20
  9. 14:16:32
  10. 25
  11. 14:16:33
  12. 30
  13. 14:16:34
  14. 35
  15. 14:16:35
  16. 40
  17. 14:16:36
  18. 14:16:37
  19. 14:16:38
  20. 14:16:39
  21. 14:16:40
  22. 45
  23. 14:16:41
  24. 14:16:42
  25. 14:16:43
  26. 14:16:44
  27. 14:16:45
  28. 50
  29. 14:16:46
  30. 14:16:47
  31. 14:16:48
  32. 14:16:49
  33. 14:16:50
  34. 55
  35. 14:16:51
  36. 14:16:52
  37. 14:16:53
  38. 14:16:54
  39. 14:16:55
  40. 0
  41. 14:16:56
  42. 5
  43. 14:16:57
  44. 10
  45. 14:16:58
  46. 15
  47. 14:16:59
  48. 20
  49. 14:17:0
  50. 14:17:1
  51. 14:17:2
  52. 14:17:3
  53. 14:17:4
  54. 14:17:5
  55. 14:17:6
  56. 14:17:7
  57. 14:17:8
  58. 14:17:9
  59. 14:17:10
  60. 14:17:11
  61. 14:17:12
  62. 14:17:13
  63. 14:17:14
  64. 14:17:15
  65. 14:17:16
  66. 14:17:17
  67. 14:17:18
  68. 14:17:19
  69. 14:17:20
  70. 25
  71. 14:17:21
  72. 14:17:22
  73. 14:17:23
  74. 14:17:24
  75. 14:17:25
  76. 30
  77. 14:17:26
  78. 14:17:27
  79. 14:17:28
  80. 14:17:29
  81. 14:17:30
  82. 35
  83. 14:17:31
  84. 14:17:32
  85. 14:17:33
  86. 14:17:34
  87. 14:17:35
  88. 40
  89. 14:17:36
  90. 14:17:37
  91. 14:17:38
  92. 14:17:39
  93. 14:17:40
  94. 45
  95. 14:17:41
  96. 14:17:42
  97. 14:17:43
  98. 14:17:44
(#) juhasz10 válasza asrock hozzászólására (») Júl 24, 2019 /
 
Mert a (nextRefreshHour <= now.second) akkortól lesz igaz.
14:17:20 now-second
25 nextRefreshHour
Itt a nextRefreshHour nem kisebb vagy egyenlő, hanem nagyobb.
(#) asrock válasza juhasz10 hozzászólására (») Júl 24, 2019 /
 
Igy jó lett egész perc 00 kezdi +5 igy pontosan 60 ki jön és nem hibázik nextRefreshHour == now.second
Köszi az észrevételt!
(#) Pulyka hozzászólása Júl 25, 2019 / 1
 
Sziasztok.

Lehet, hogy elég amatőr hiba, de azért leírom, mivel szembesültem, hátha másnak segíthetek.
Hestoreból vásároltam max7219 által vezérelt kijelzőt. Arduino nanoval készítettem már saját shieldet, FET-kel, sorkapcsokkal, stb, azt járatom 12V-ról, mert közvetlenül a panel kapcsol 12V-os fogyasztókat.(arduino kimenet után optokapuval, és sot23-as FET-tel.) 12V a shieldnek, és mivel más 5V-os fogyasztókat is táplálok, vásároltam egy ilyen step down-ot is. Nem tetszett a max7219-es kijelző KIT-nek, hogy én a step down által szolgáltatott 5V-ról akarom járatni, míg az arduinót 12V-ról.(amiben ugye ott a beépített 5V-os stabilizátor, de arra nem akartam még ráakasztani a KIT-et, mert megfőtt volna szegény.) Egyébként nem volt füst, csak a kijelző össze-vissza mutatott mindent, majd elsötétült.
Megoldás: Ugyanarról az 5V-ról járatni az arduinót, és a kijelzőt, de már erről az erős step down egységről.

UI: ..egyébként a nevezett step downnak elég kaki a trimmer potija, legalábbis nyomás/érintésérzékeny.
A hozzászólás módosítva: Júl 25, 2019
(#) KoblogPerGyok válasza Pulyka hozzászólására (») Júl 25, 2019 /
 
Szia!

Közös volt a Föld?
(#) proba válasza Pulyka hozzászólására (») Júl 26, 2019 /
 
Mondjuk azzal szoktam kezdeni a hasonló step-down szabályzóknál, megkeresem a panelen az osztó ellenállást, és igyekszem úgy módosítani, hogy a beállítható feszültség maximuma, a kívánt szint környékére essen. Így ha tekergetés, vagy bármi miatt megszakad a poti, akkor sem válik füstté semmi.
(#) sargarigo válasza proba hozzászólására (») Júl 26, 2019 /
 
Meg menet közben amúgy sem tekergetjük az lcd, stb tápját. Előre be kell állítani, aztán egy csepp lakkot a potira! Ekkor lehet rátenni a fogyasztót. Mondjuk mindig van új amit meg lehet tanulni (mindenkinek).
(#) Rober_4 hozzászólása Júl 26, 2019 /
 
Sziasztok! Megoldottam a szinusztömbös számítást.
Még az ini részben feltöltöttem egy 360 elemű tömböt (sinusfg) a fokhoz tartozó sinus értékek 1000 szeresével. Tehát a maximum értékem 1000, a minimum értékem -1000.
Maga a függvény nálam két paramétert kap:
Az idő megadja, hogy az éppen aktuális hangminta melyik pozícióban van a periódusidőhöz képest, (tulajdonképpen ez a fokkal arányos), az idomax pedig a hangminta periódusideje, ez a lenyomott billentyűhöz tartozó frekvenciától függ. Az értéke a legmélyebb hangnál: 1087, a legmagasabbnál 2. Egyik sem lehet negatív.
A függvényem így néz ki:

  1. int sinus (uint16_t ido, uint16_t idomax) {
  2.   return  sinusfg[ido * 360 / idomax];
  3. }


A kérdésem csak annyi, lehet-e gyorsabban?
Ugye ha előre kiszámolom az osztást, akkor egy törttel kell szoroznom, és a segédváltozónak is double-nek kell lennie.
Minden segítséget köszönök!

Egyébként már 4hang polifóniát tudok hangonként két generátorral, és egész érdekes hangok kezdenek kialakulni...
A hozzászólás módosítva: Júl 26, 2019
(#) Johnycorp válasza Pulyka hozzászólására (») Júl 26, 2019 /
 
Szia.

Ahogy már más is írta a GND pont legyen közös, mert egyébként nem fog működni a dolog.

Másfelől én az ilyen és hasonló (pl: LM2596) step-down áramköröknél (készen vett modulok) beállítom / kiszámolom a szükséges ellenállást és az megy a poti helyére. Problémaforrás megoldva.
Meg persze a kimenetre minden esetben van kötve védelem (szupresszor / varisztor).
(#) Rober_4 válasza Rober_4 hozzászólására (») Júl 27, 2019 / 3
 
Hátha esetleg valakit érdekel, ez egy 4 hangig polifonikus szinti. A kimenete az Arduino Due DAC-ja. A midi jelet a serial2-ről fogadja külső billentyűzetről, optocsatolón keresztül. Kijelzője nincs, sajnos az nagyon lelassította. Kókányoltam bele valami fm modulációs függvényt. Illetve ha nem fm módban indítjuk, akkor 4 alaposzcillátor (szinusz, háromszög, fűrész, négyszög vagy zaj) vezérelhető, háromfajta TVA, TVF , Zaj görbével, illetve egy pich-görbével. Ezek elég darabosak még. A továbbiakban a paraméterek állítását tervezem megoldani, MIDI-n azaz serial2 porton keresztül, szabványos, kontrollerparaméterekkel. Mással biztos nem fog foglakozni ez a board, ha teszek rá kijelzőt illetve vezérlést, azt egy külső egységről fogom mondjuk egy Arduino nanoval meghajtani stb.
Csatoltam egy mintát, a jelenlegi hangjáról, remélem maradhat. Természetesen tettem rá zengetőt utólag.
A program szintén csatolva. Bármilyen észrevételt szívesen fogadok.
(#) djusee válasza Rober_4 hozzászólására (») Júl 27, 2019 /
 
Zene bejön, olyan Jean Michel Jarre -os.
(#) kapu48 válasza Rober_4 hozzászólására (») Júl 27, 2019 / 2
 
Szia!
Érdekelnek a fejlemények! Majd rakok a DUE-re valami erősítőt, hogy meg tudjam hallgatni.

Panaszkodsz, hogy lassú a gép!
Ezeket a függvényeket sokat hívod, én definíciókba raktam őket.
Így közvetlen befordulnak a hívások helyére, kicsit több helyet foglalnak, de valamivel gyorsabb lesz a futás idő.
/* Az eredeti függvényeket természetesen rakjad megjegyzés blokkba. */
// Eredeti méret: 34528 6%, Módosított méret: 34720 - 6%

  1. #include <Audio.h>
  2. //#include <LiquidCrystal_I2C.h>
  3. //LiquidCrystal_I2C lcd(0x27, 16, 2);
  4.  
  5. #define sinus(ido, idomax)  (sinusfg[ido * 360 / idomax])
  6. #define haromszog(ido, idomax)  (haromszogfg[ido * 360 / idomax])
  7. #define furesz(ido, idomax)  (fureszfg[ido * 360 / idomax])
  8. #define negyszog(ido, idomax)  (negyszogfg[ido * 360 / idomax])
  9. #define zaj(ido, idomax)  (zajfg[ido * 360 / idomax])
  10. #define fm(ido, idomax, lev1, ido2, idomax2, lev2)  (sinusfg[(ido * 360 / idomax + sinusfg[ido2 * 360 / idomax2] / lev2) % 360])
  11. ...


Véleményed?
Következő: »»   545 / 838
Bejelentkezés

Belépés

Hirdetés
Lapoda.hu     XDT.hu     HEStore.hu
Az oldalon sütiket használunk a helyes működéshez. Bővebb információt az adatvédelmi szabályzatban olvashatsz. Megértettem