Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

560 lignes
18 KiB

  1. #include <stdbool.h>
  2. #include <avr/io.h>
  3. #include <avr/interrupt.h>
  4. #define F_CPU 20000000UL //20 MHz
  5. #include <util/delay.h>
  6. /* 7-Segment display: */
  7. #define NUM_DIGITS 3U //3 Digits
  8. #define DIGIT_TENS 2U //left
  9. #define DIGIT_ONES 1U //middle
  10. #define DIGIT_UNIT 0U //right
  11. #define SELECT_DIGIT(digit) (1U << digit)
  12. /* DS18B20 peripherals: */
  13. #define DS18B20_PORT PORTC
  14. #define DS18B20_DDR DDRC
  15. #define DS18B20_PIN PINC
  16. #define DS18B20_DQ PINC3
  17. /* DS18B20 commands: */
  18. #define DS18B20_CMD_CONVERTTEMP 0x44U
  19. #define DS18B20_CMD_RSCRATCHPAD 0xBEU
  20. #define DS18B20_CMD_WSCRATCHPAD 0x4EU
  21. #define DS18B20_CMD_CPYSCRATCHPAD 0x48U
  22. #define DS18B20_CMD_RECEEPROM 0xB8U
  23. #define DS18B20_CMD_RPWRSUPPLY 0xB4U
  24. #define DS18B20_CMD_SEARCHROM 0xF0U
  25. #define DS18B20_CMD_READROM 0x33U
  26. #define DS18B20_CMD_MATCHROM 0x55U
  27. #define DS18B20_CMD_SKIPROM 0xCCU
  28. #define DS18B20_CMD_ALARMSEARCH 0xECU
  29. //#define TEMP_MIN 0U
  30. //#define TEMP_MAX 50U
  31. #define MODE_MEASURE 0U
  32. #define MODE_ANIMATION_1 1U
  33. #define MODE_ANIMATION_2 2U
  34. #define ANIMATION_DELAY_MS 70U
  35. #define ANIMATION_START_DELAY_MS 70U
  36. #define BUTTON_THRESHOLD 50 //software debounce
  37. /* Symbol indexes: */
  38. #define SYM_n 10U
  39. #define SYM_o 11U
  40. #define SYM_E 12U
  41. #define SYM_R 13U
  42. #define SYM_DEG_CELCIUS 14U
  43. #define SYM_P_P 15U
  44. #define SYM_OFF 16U
  45. #define SYM_DASH 17U
  46. #define SYM_I 18U
  47. #define SYM_SEG_A 19U
  48. #define SYM_SEG_B 20U
  49. #define SYM_SEG_C 21U
  50. #define SYM_SEG_D 22U
  51. #define SYM_SEG_E 23U
  52. #define SYM_SEG_F 24U
  53. #define SYM_SEG_AB 25U
  54. #define SYM_SEG_BCD 26U
  55. #define SYM_SEG_CD 27U
  56. #define SYM_SEG_DE 28U
  57. #define SYM_SEG_DEF 29U
  58. #define SYM_SEG_AEF 30U
  59. #define SYM_SEG_AF 31U
  60. /*
  61. 7-Segment Layout:
  62. A
  63. F B
  64. G
  65. E C
  66. D P
  67. Pin 7 6 5 4 3 2 1 0
  68. Segment A F B E D C G P
  69. */
  70. static uint8_t g_au8Symbols[32U] =
  71. {
  72. /* 00 */ 0b11111100U, //0 (ABCDEF)
  73. /* 01 */ 0b00100100U, //1 (BC)
  74. /* 02 */ 0b10111010U, //2 (ABDEG)
  75. /* 03 */ 0b10101110U, //3 (ABCDG)
  76. /* 04 */ 0b01100110U, //4 (BCFG)
  77. /* 05 */ 0b11001110U, //5 (ACDFG)
  78. /* 06 */ 0b11011110U, //6 (ACDEFG)
  79. /* 07 */ 0b10100100U, //7 (ABC)
  80. /* 08 */ 0b11111110U, //8 (ABCDEFG)
  81. /* 09 */ 0b11101110U, //9 (ABCDFG)
  82. /* 10 */ 0b00010110U, //n (CEG) SYM_n
  83. /* 11 */ 0b00011110U, //o (CDEG) SYM_o
  84. /* 12 */ 0b11011010U, //E (ADEFG) SYM_E
  85. /* 13 */ 0b00010010U, //r (EG) SYM_r
  86. /* 14 */ 0b10101101U, //degC (ABCDP) SYM_DEG_CELCIUS
  87. /* 15 */ 0b11110011U, //P. (ABEFGP) SYM_P_P
  88. /* 16 */ 0b00000000U, //OFF SYM_OFF
  89. /* 17 */ 0b00000010U, //- (G) SYM_DASH
  90. /* 18 */ 0b01010000U, //I (EF) SYM_I
  91. /* 19 */ 0b10000000U, //SegA SYM_SEG_A
  92. /* 20 */ 0b00100000U, //SegB SYM_SEG_B
  93. /* 21 */ 0b00000100U, //SegC SYM_SEG_C
  94. /* 22 */ 0b00001000U, //SegD SYM_SEG_D
  95. /* 23 */ 0b00010000U, //SegE SYM_SEG_E
  96. /* 24 */ 0b01000000U, //SegF SYM_SEG_F
  97. /* 25 */ 0b10100000U, //SegAB SYM_SEG_AB
  98. /* 26 */ 0b00101100U, //SegBCD SYM_SEG_BCD
  99. /* 27 */ 0b00001100U, //SegCD SYM_SEG_CD
  100. /* 28 */ 0b00011000U, //SegDE SYM_SEG_DE
  101. /* 29 */ 0b01011000U, //SegDEF SYM_SEG_DEF
  102. /* 30 */ 0b11010000U, //SegAEF SYM_SEG_AEF
  103. /* 31 */ 0b11000000U, //SegAF SYM_SEG_AF
  104. };
  105. //static uint8_t g_u8WaterTemperature = 24U; //Water temperature, default 24 degree celcius
  106. static uint8_t g_u8ButtonCountTop = 0U; //upper button counter
  107. static uint8_t g_u8ButtonCountBot = 0U; //lower button counter
  108. static uint8_t g_u8Mode = MODE_MEASURE; //Current display mode
  109. uint8_t g_u8ISRDigitSelect = 0U;
  110. uint8_t g_au8Digits[3U] =
  111. {
  112. SYM_OFF, //unit
  113. SYM_OFF, //ones
  114. SYM_OFF //tens
  115. };
  116. void s_vInit( void );
  117. void s_vSetValue( uint8_t u8Value, uint8_t u8Unit );
  118. void s_vSetDisplayDigits( uint8_t u8Tens, uint8_t u8Ones, uint8_t u8Unit );
  119. void s_vReadButtons( void );
  120. void s_vShowAnim( uint8_t u8Select );
  121. uint8_t s_u8DS18B20_Reset( void );
  122. void s_vDS18B20_WriteByte( uint8_t u8Byte );
  123. void s_vDS18B20_WriteBit( uint8_t u8Bit );
  124. uint8_t s_u8DS18B20_ReadByte( void );
  125. uint8_t s_u8DS18B20_ReadBit( void );
  126. /************************************************************************/
  127. /* Timer1 ISR - show values on display */
  128. /************************************************************************/
  129. ISR ( TIMER1_COMPA_vect )
  130. {
  131. PORTB = 0;
  132. PORTD = 0;
  133. _delay_us(2);
  134. PORTB = SELECT_DIGIT(g_u8ISRDigitSelect);
  135. PORTD = g_au8Symbols[g_au8Digits[g_u8ISRDigitSelect]];
  136. g_u8ISRDigitSelect++;
  137. if (g_u8ISRDigitSelect > 2U)
  138. {
  139. g_u8ISRDigitSelect = 0U;
  140. }
  141. /* Read and evaluate button status */
  142. s_vReadButtons();
  143. }
  144. /************************************************************************/
  145. /* main() */
  146. /************************************************************************/
  147. int main(void)
  148. {
  149. uint8_t u8TempLo, u8TempHi;
  150. float f32Temperature;
  151. uint8_t u8Loop = 0U;
  152. /* Init I/O: */
  153. s_vInit();
  154. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_OFF );
  155. _delay_ms(10);
  156. s_vSetDisplayDigits( SYM_SEG_A, SYM_OFF, SYM_OFF );
  157. _delay_ms(300);
  158. s_vSetDisplayDigits( SYM_SEG_A, SYM_SEG_A, SYM_OFF );
  159. _delay_ms(300);
  160. s_vSetDisplayDigits( SYM_SEG_A, SYM_SEG_A, SYM_SEG_D );
  161. _delay_ms(300);
  162. while ( u8Loop < 5U )
  163. {
  164. s_vSetDisplayDigits( SYM_OFF, SYM_SEG_A, SYM_SEG_DE );
  165. _delay_ms(ANIMATION_START_DELAY_MS);
  166. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_SEG_DEF );
  167. _delay_ms(ANIMATION_START_DELAY_MS);
  168. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_SEG_AEF );
  169. _delay_ms(ANIMATION_START_DELAY_MS);
  170. s_vSetDisplayDigits( SYM_OFF, SYM_SEG_D, SYM_SEG_AF );
  171. _delay_ms(ANIMATION_START_DELAY_MS);
  172. s_vSetDisplayDigits( SYM_SEG_D, SYM_SEG_D, SYM_SEG_A );
  173. _delay_ms(ANIMATION_START_DELAY_MS);
  174. s_vSetDisplayDigits( SYM_SEG_DE, SYM_SEG_D, SYM_OFF );
  175. _delay_ms(ANIMATION_START_DELAY_MS);
  176. s_vSetDisplayDigits( SYM_SEG_DEF, SYM_OFF, SYM_OFF );
  177. _delay_ms(ANIMATION_START_DELAY_MS);
  178. s_vSetDisplayDigits( SYM_SEG_AEF, SYM_OFF, SYM_OFF );
  179. _delay_ms(ANIMATION_START_DELAY_MS);
  180. s_vSetDisplayDigits( SYM_SEG_AF, SYM_SEG_A, SYM_OFF );
  181. _delay_ms(ANIMATION_START_DELAY_MS);
  182. s_vSetDisplayDigits( SYM_SEG_A, SYM_SEG_A, SYM_SEG_D );
  183. _delay_ms(ANIMATION_START_DELAY_MS);
  184. u8Loop++;
  185. }
  186. /* Set default value: */
  187. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_OFF );
  188. while ( 1 )
  189. {
  190. /* perform mode action */
  191. switch ( g_u8Mode )
  192. {
  193. case MODE_MEASURE:
  194. (void) s_u8DS18B20_Reset();
  195. s_vDS18B20_WriteByte( DS18B20_CMD_SKIPROM );
  196. s_vDS18B20_WriteByte( DS18B20_CMD_CONVERTTEMP );
  197. while ( !s_u8DS18B20_ReadBit() );
  198. (void) s_u8DS18B20_Reset();
  199. s_vDS18B20_WriteByte( DS18B20_CMD_SKIPROM );
  200. s_vDS18B20_WriteByte( DS18B20_CMD_RSCRATCHPAD );
  201. u8TempLo = s_u8DS18B20_ReadByte();
  202. u8TempHi = s_u8DS18B20_ReadByte();
  203. f32Temperature = ((u8TempHi << 8) + u8TempLo) * 0.0625;
  204. s_vSetValue( (uint8_t) f32Temperature, SYM_DEG_CELCIUS );
  205. if ( g_u8Mode == MODE_MEASURE )
  206. {
  207. _delay_ms(500);
  208. }
  209. break;
  210. case MODE_ANIMATION_1:
  211. s_vShowAnim(g_u8Mode);
  212. g_u8Mode = MODE_MEASURE;
  213. break;
  214. case MODE_ANIMATION_2:
  215. s_vShowAnim(g_u8Mode);
  216. g_u8Mode = MODE_MEASURE;
  217. break;
  218. default:
  219. break;
  220. } //end switch
  221. }
  222. return 0;
  223. }
  224. /************************************************************************/
  225. /* Initialize inputs and outputs */
  226. /************************************************************************/
  227. void s_vInit( void )
  228. {
  229. /* Port D - Pins 0-7: Outputs */
  230. DDRD = 0b11111111U;
  231. /* Port B - Pins 0-2: Outputs */
  232. DDRB = 0b00000111U;
  233. /* Port C - Pins 4-5: Inputs with internal Pull-up */
  234. PORTC = 0b00110000U;
  235. /* 16-bit TIMER1 in CTC mode
  236. * prescaler = 8
  237. * 20 MHz / 8 = 2.5 MHz
  238. * Compare value = 2500 => 1 ms IRQ
  239. */
  240. TCCR1A = 0U;
  241. TCCR1B = (1<<WGM12) | (1<<CS11);
  242. OCR1A = 2500U;
  243. TIMSK1 |= (1<<OCIE1A);
  244. sei();
  245. return; //void
  246. }
  247. /************************************************************************/
  248. /* Set new values to be shown on display */
  249. /************************************************************************/
  250. void s_vSetValue( uint8_t u8Value, uint8_t u8Unit )
  251. {
  252. uint8_t u8Tmp = u8Value / 10U;
  253. s_vSetDisplayDigits(u8Tmp, u8Value - (u8Tmp * 10U), u8Unit);
  254. return; //void
  255. }
  256. /************************************************************************/
  257. /* Set display digits individually */
  258. /************************************************************************/
  259. void s_vSetDisplayDigits( uint8_t u8Tens, uint8_t u8Ones, uint8_t u8Unit )
  260. {
  261. g_au8Digits[DIGIT_TENS] = u8Tens;
  262. g_au8Digits[DIGIT_ONES] = u8Ones;
  263. g_au8Digits[DIGIT_UNIT] = u8Unit;
  264. return; //void
  265. }
  266. /************************************************************************/
  267. /* Read button status */
  268. /************************************************************************/
  269. void s_vReadButtons( void )
  270. {
  271. if ( !(PINC & (1<<PINC4)) ) //if button '+' pressed
  272. {
  273. g_u8ButtonCountTop++;
  274. }
  275. else
  276. {
  277. g_u8ButtonCountTop = 0U; //reset button counter
  278. }
  279. if ( !(PINC & (1<<PINC5)) ) //if button '-' pressed
  280. {
  281. g_u8ButtonCountBot++;
  282. }
  283. else
  284. {
  285. g_u8ButtonCountBot = 0U; //reset button counter
  286. }
  287. if ( g_u8ButtonCountTop > BUTTON_THRESHOLD )
  288. {
  289. g_u8ButtonCountTop = 0U; //reset top counter
  290. g_u8Mode = MODE_ANIMATION_1;
  291. }
  292. else if ( g_u8ButtonCountBot > BUTTON_THRESHOLD )
  293. {
  294. g_u8ButtonCountBot = 0U; //reset bottom counter
  295. g_u8Mode = MODE_ANIMATION_2;
  296. }
  297. //else
  298. //{
  299. //return MODE_MEASURE;
  300. //}
  301. return; //void
  302. }
  303. /************************************************************************/
  304. /* */
  305. /************************************************************************/
  306. void s_vShowAnim( uint8_t u8Select )
  307. {
  308. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_OFF );
  309. _delay_ms(ANIMATION_DELAY_MS);
  310. switch ( u8Select )
  311. {
  312. case MODE_ANIMATION_1: //fade from left to right and from right to left
  313. s_vSetDisplayDigits( SYM_I, SYM_OFF, SYM_OFF );
  314. _delay_ms(ANIMATION_DELAY_MS);
  315. s_vSetDisplayDigits( SYM_E, SYM_OFF, SYM_OFF );
  316. _delay_ms(ANIMATION_DELAY_MS);
  317. s_vSetDisplayDigits( 8U, SYM_OFF, SYM_OFF );
  318. _delay_ms(ANIMATION_DELAY_MS);
  319. s_vSetDisplayDigits( 8U, SYM_I, SYM_OFF );
  320. _delay_ms(ANIMATION_DELAY_MS);
  321. s_vSetDisplayDigits( 8U, SYM_E, SYM_OFF );
  322. _delay_ms(ANIMATION_DELAY_MS);
  323. s_vSetDisplayDigits( 8U, 8U, SYM_OFF );
  324. _delay_ms(ANIMATION_DELAY_MS);
  325. s_vSetDisplayDigits( 8U, 8U, 1U );
  326. _delay_ms(ANIMATION_DELAY_MS);
  327. s_vSetDisplayDigits( 8U, 8U, 3U );
  328. _delay_ms(ANIMATION_DELAY_MS);
  329. s_vSetDisplayDigits( 8U, 8U, 8U );
  330. _delay_ms(ANIMATION_DELAY_MS);
  331. s_vSetDisplayDigits( 8U, 8U, 8U );
  332. _delay_ms(ANIMATION_DELAY_MS);
  333. s_vSetDisplayDigits( 8U, 8U, 3U );
  334. _delay_ms(ANIMATION_DELAY_MS);
  335. s_vSetDisplayDigits( 8U, 8U, 1U );
  336. _delay_ms(ANIMATION_DELAY_MS);
  337. s_vSetDisplayDigits( 8U, 8U, SYM_OFF );
  338. _delay_ms(ANIMATION_DELAY_MS);
  339. s_vSetDisplayDigits( 8U, SYM_E, SYM_OFF );
  340. _delay_ms(ANIMATION_DELAY_MS);
  341. s_vSetDisplayDigits( 8U, SYM_I, SYM_OFF );
  342. _delay_ms(ANIMATION_DELAY_MS);
  343. s_vSetDisplayDigits( 8U, SYM_OFF, SYM_OFF );
  344. _delay_ms(ANIMATION_DELAY_MS);
  345. s_vSetDisplayDigits( SYM_E, SYM_OFF, SYM_OFF );
  346. _delay_ms(ANIMATION_DELAY_MS);
  347. s_vSetDisplayDigits( SYM_I, SYM_OFF, SYM_OFF );
  348. _delay_ms(ANIMATION_DELAY_MS);
  349. break;
  350. case MODE_ANIMATION_2: //fade from left to right and clear
  351. s_vSetDisplayDigits( SYM_I, SYM_OFF, SYM_OFF );
  352. _delay_ms(ANIMATION_DELAY_MS);
  353. s_vSetDisplayDigits( SYM_E, SYM_OFF, SYM_OFF );
  354. _delay_ms(ANIMATION_DELAY_MS);
  355. s_vSetDisplayDigits( 8U, SYM_OFF, SYM_OFF );
  356. _delay_ms(ANIMATION_DELAY_MS);
  357. s_vSetDisplayDigits( 8U, SYM_I, SYM_OFF );
  358. _delay_ms(ANIMATION_DELAY_MS);
  359. s_vSetDisplayDigits( 8U, SYM_E, SYM_OFF );
  360. _delay_ms(ANIMATION_DELAY_MS);
  361. s_vSetDisplayDigits( 8U, 8U, SYM_OFF );
  362. _delay_ms(ANIMATION_DELAY_MS);
  363. s_vSetDisplayDigits( 8U, 8U, 1U );
  364. _delay_ms(ANIMATION_DELAY_MS);
  365. s_vSetDisplayDigits( 8U, 8U, 3U );
  366. _delay_ms(ANIMATION_DELAY_MS);
  367. s_vSetDisplayDigits( 8U, 8U, 8U );
  368. _delay_ms(ANIMATION_DELAY_MS);
  369. s_vSetDisplayDigits( 3U, 8U, 8U );
  370. _delay_ms(ANIMATION_DELAY_MS);
  371. s_vSetDisplayDigits( 1U, 8U, 8U );
  372. _delay_ms(ANIMATION_DELAY_MS);
  373. s_vSetDisplayDigits( SYM_OFF, 8U, 8U );
  374. _delay_ms(ANIMATION_DELAY_MS);
  375. s_vSetDisplayDigits( SYM_OFF, 3U, 8U );
  376. _delay_ms(ANIMATION_DELAY_MS);
  377. s_vSetDisplayDigits( SYM_OFF, 1U, 8U );
  378. _delay_ms(ANIMATION_DELAY_MS);
  379. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, 8U );
  380. _delay_ms(ANIMATION_DELAY_MS);
  381. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_E );
  382. _delay_ms(ANIMATION_DELAY_MS);
  383. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_I );
  384. _delay_ms(ANIMATION_DELAY_MS);
  385. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_OFF );
  386. _delay_ms(ANIMATION_DELAY_MS);
  387. break;
  388. default:
  389. break;
  390. } //end switch
  391. s_vSetDisplayDigits( SYM_OFF, SYM_OFF, SYM_OFF );
  392. _delay_ms(ANIMATION_DELAY_MS);
  393. return; //void
  394. }
  395. /************************************************************************/
  396. /* Reset DS18B20 (960 us) */
  397. /************************************************************************/
  398. uint8_t s_u8DS18B20_Reset( void )
  399. {
  400. uint8_t u8Status;
  401. //low for 480 us
  402. DS18B20_PORT &= ~ (1<<DS18B20_DQ); //low
  403. DS18B20_DDR |= (1<<DS18B20_DQ); //output
  404. _delay_us(480);
  405. //release line and wait for 60 us
  406. DS18B20_DDR &= ~(1<<DS18B20_DQ); //input
  407. _delay_us(60);
  408. //get value and wait 420 us
  409. u8Status = (DS18B20_PIN & (1<<DS18B20_DQ));
  410. _delay_us(420);
  411. //return the read value: 0=okay, 1=error
  412. return u8Status;
  413. }
  414. /************************************************************************/
  415. /* Write byte to DS18B20 (8 * 61 us = 488 us) */
  416. /************************************************************************/
  417. void s_vDS18B20_WriteByte( uint8_t u8Byte )
  418. {
  419. uint8_t u8Bits = 8U;
  420. while ( u8Bits-- )
  421. {
  422. s_vDS18B20_WriteBit( u8Byte & 1U );
  423. u8Byte >>= 1U;
  424. }
  425. return; //void
  426. }
  427. /************************************************************************/
  428. /* Write single bit to DS18B20 (61 us) */
  429. /************************************************************************/
  430. void s_vDS18B20_WriteBit( uint8_t u8Bit )
  431. {
  432. //low for 1 us
  433. DS18B20_PORT &= ~ (1U<<DS18B20_DQ); //low
  434. DS18B20_DDR |= (1U<<DS18B20_DQ); //output
  435. _delay_us(1);
  436. //if we want to write 1, release the line (if not will keep low)
  437. if (1U == u8Bit )
  438. {
  439. DS18B20_DDR &= ~(1<<DS18B20_DQ); //input
  440. }
  441. //wait 60 us and release the line
  442. _delay_us(60);
  443. DS18B20_DDR &= ~(1<<DS18B20_DQ); //input
  444. return; //void
  445. }
  446. /************************************************************************/
  447. /* Read byte from DS18B20 (8 * 60 us = 480 us) */
  448. /************************************************************************/
  449. uint8_t s_u8DS18B20_ReadByte( void )
  450. {
  451. uint8_t u8Bits = 8U;
  452. uint8_t n = 0U;
  453. while ( u8Bits-- )
  454. {
  455. n >>= 1U;
  456. n |= (s_u8DS18B20_ReadBit()<<7U);
  457. }
  458. return n;
  459. }
  460. /************************************************************************/
  461. /* Read single bit from DS18B20 (60 us) */
  462. /************************************************************************/
  463. uint8_t s_u8DS18B20_ReadBit( void )
  464. {
  465. uint8_t u8Bit = 0U;
  466. //low for 1 us
  467. DS18B20_PORT &= ~ (1U<<DS18B20_DQ); //low
  468. DS18B20_DDR |= (1U<<DS18B20_DQ); //output
  469. _delay_us(1);
  470. //release line and wait for 14 us
  471. DS18B20_DDR &= ~(1U<<DS18B20_DQ); //input
  472. _delay_us(14);
  473. //read the value
  474. if ( DS18B20_PIN & (1U<<DS18B20_DQ) )
  475. {
  476. u8Bit = 1U;
  477. }
  478. //wait 45 us and return read value
  479. _delay_us(45);
  480. return u8Bit;
  481. }