Mercurial > public > hwos_code
annotate src/hwos.asm @ 628:cd58f7fc86db
3.05 stable work
author | heinrichsweikamp |
---|---|
date | Thu, 19 Sep 2019 12:01:29 +0200 |
parents | bf5fee575701 |
children | 237931377539 |
rev | line source |
---|---|
0 | 1 ;============================================================================= |
2 ; | |
628 | 3 ; File hwos.asm combined next generation V3.03.7 |
0 | 4 ; |
275 | 5 ; Definition of the hwOS dive computer platform. |
0 | 6 ; |
7 ; Copyright (c) 2011, JD Gascuel, HeinrichsWeikamp, all right reserved. | |
8 ;============================================================================= | |
9 ; HISTORY | |
604 | 10 ; 2011-05-24 : [jDG] Cleanups from initial Matthias code |
11 ; 2011-06-24 : [MH] Added clock speeds | |
12 | |
623 | 13 ;============================================================================= |
14 | |
15 #DEFINE ACCESS_RAM_VARS ; the access RAM variables are declared in this file | |
604 | 16 |
275 | 17 #include "hwos.inc" |
623 | 18 #include "eeprom_rs232.inc" |
0 | 19 |
623 | 20 ;----------------------------- CONFIG ---------------------------------------- |
21 CONFIG RETEN = OFF ; disabled - controlled by SRETEN bit | |
604 | 22 CONFIG SOSCSEL = HIGH ; High Power SOSC circuit selected |
623 | 23 CONFIG XINST = OFF ; code won't execute in extended mode |
24 CONFIG FOSC = INTIO2 ; internal RC oscillator, no clock-out | |
25 CONFIG PLLCFG = OFF ; | |
26 CONFIG IESO = OFF ; disabled | |
27 CONFIG PWRTEN = OFF ; disabled, because incompatible with ICD3 (Ri-400) | |
28 CONFIG BOREN = ON ; controlled with SBOREN bit | |
29 CONFIG BORV = 2 ; 2.0V | |
30 CONFIG BORPWR = MEDIUM ; BORMV set to medium power level | |
31 CONFIG WDTEN = ON ; WDT controlled by SWDTEN bit setting | |
32 CONFIG WDTPS = 128 ; 1:128 | |
33 CONFIG RTCOSC = SOSCREF ; RTCC uses SOSC | |
34 CONFIG MCLRE = ON ; MCLR Enabled, RG5 Disabled | |
35 CONFIG CCP2MX = PORTBE ; RE7 micro-controller mode/RB3-all other modes | |
36 | |
37 | |
38 ;---------------------------- Bank0 ACCESS RAM ------------------------------- | |
39 ac_ram equ 0x000 | |
40 ac_ram udata_acs ac_ram ; access RAM data | |
41 | |
42 | |
43 ;---- Flags - Hardware Descriptors | |
44 HW_descriptor res 1 ; OSTC - model descriptor (cleared & rebuilt in restart) | |
45 HW_variants res 1 ; OSTC - model variants (NOT cleared in restart) | |
628 | 46 |
623 | 47 ;---- Flags - Hardware States |
628 | 48 HW_flags_state1 res 1 ; hardware - states 1 |
49 HW_flags_state2 res 1 ; hardware - states 2 | |
623 | 50 |
51 ;--- Flags - Operating System | |
52 OS_flags_persist res 1 ; system - persistent settings (NOT cleared in restart) | |
53 OS_flags_ISR1 res 1 ; system - ISR control 1 | |
54 OS_flags_ISR2 res 1 ; system - ISR control 2 | |
55 | |
56 ;---- Flags - Operating Modes | |
57 OM_flags_mode res 1 ; operating modes | |
58 | |
59 ;---- Flags - Dive Modes | |
60 DM_flags_deco res 1 ; dive mode - main dive & deco mode | |
61 | |
62 ;---- CPU Speed | |
63 cpu_speed_request res 1 ; requested CPU speed: =1: eco, =2: normal, =3: fastest | |
64 cpu_speed_state res 1 ; current CPU speed: =1: eco, =2: normal, =3: fastest | |
65 | |
66 ;---- Timebase & Eventbase | |
67 timebase res 1 ; timed trigger flags and running timebase | |
68 eventbase res 1 ; event trigger flags | |
69 | |
70 ;---- Timeout-Timer Service | |
71 isr_timeout_timer res 1 ; timeout timer | |
72 isr_timeout_reload res 1 ; timeout reload value | |
73 | |
74 ;---- Dive Times | |
75 total_divetime_secs res 2 ; total dive time, seconds | |
76 counted_divetime_mins res 2 ; counted dive time, minutes | Attention: do not change the position of | |
77 counted_divetime_secs res 1 ; counted dive time, seconds | these 2 Variables relative to each other! | |
604 | 78 |
623 | 79 ;---- Dive Times / Apnoe |
80 apnoe_surface_mins res 1 ; surface time minutes | Attention: do not change the position of | |
81 apnoe_surface_secs res 1 ; surface time seconds | these 2 Variables relative to each other! | |
82 | |
83 apnoe_dive_mins res 1 ; dive time minutes | Attention: do not change the position of | |
84 apnoe_dive_secs res 1 ; dive time seconds | these 2 Variables relative to each other! | |
85 | |
86 | |
87 ;---- Profile Recording | |
88 sampling_rate res 1 ; configured sampling rate | |
89 sampling_timer res 1 ; sampling timer | |
90 | |
91 ;---- Simulator Mode | |
92 simulatormode_depth res 1 ; depth in simulator mode | |
93 | |
94 ;---- HUD / Sensor Data | |
95 hud_status_byte res 1 ; HUD status byte, see definition of flags | Attention: keep relative position | |
96 hud_battery_mv res 2 ; hud/ppo2 monitor battery voltage in mV | between these two variables! | |
97 | |
98 | |
99 ; 28 byte user data | |
100 ; 32 byte tmp data placed by C compiler | |
101 ; 20 byte variables placed by math library | |
102 ; == | |
628 | 103 ; 80 byte used, 16 byte free (96 byte total available) |
623 | 104 |
105 global HW_descriptor | |
106 global HW_variants | |
628 | 107 global HW_flags_state1 |
108 global HW_flags_state2 | |
623 | 109 global OS_flags_persist |
110 global OS_flags_ISR1 | |
111 global OS_flags_ISR2 | |
112 global OM_flags_mode | |
113 global DM_flags_deco | |
114 global cpu_speed_request | |
115 global cpu_speed_state | |
116 global timebase | |
117 global eventbase | |
118 global isr_timeout_timer | |
119 global isr_timeout_reload | |
120 global total_divetime_secs | |
121 global counted_divetime_mins | |
122 global counted_divetime_secs | |
123 global apnoe_surface_secs | |
124 global apnoe_surface_mins | |
125 global apnoe_dive_secs | |
126 global apnoe_dive_mins | |
127 global sampling_rate | |
128 global sampling_timer | |
129 global simulatormode_depth | |
130 global hud_status_byte | |
131 global hud_battery_mv | |
132 | |
133 ;----------------------------------------------------------------------------- | |
134 | |
135 hwos CODE | |
604 | 136 |
0 | 137 ;============================================================================= |
138 | |
604 | 139 global init_ostc |
275 | 140 init_ostc: |
623 | 141 |
142 ; Oscillator | |
143 banksel common ; select bank common | |
0 | 144 movlw b'01110010' |
604 | 145 movwf OSCCON ; 16 MHz INTOSC |
0 | 146 movlw b'00001000' |
604 | 147 movwf OSCCON2 ; secondary oscillator running |
0 | 148 movlw b'00000000' |
604 | 149 movwf OSCTUNE ; 4x PLL disable (Bit 6) - only works with 8 or 16MHz (=32 or 64MHz) |
608 | 150 |
623 | 151 movlw coding_speed_normal ; coding for normal CPU speed |
152 movwf cpu_speed_request ; store CPU shall run with normal speed | |
153 movwf cpu_speed_state ; store CPU does run with normal speed | |
608 | 154 |
623 | 155 ;bcf RCON,SBOREN ; brown-out off (not needed here, is handled in bootloader) |
604 | 156 bcf RCON,IPEN ; priority interrupts off |
608 | 157 |
604 | 158 banksel WDTCON |
159 movlw b'10000000' | |
160 movwf WDTCON ; setup watchdog | |
0 | 161 |
608 | 162 |
0 | 163 ; I/O Ports |
604 | 164 banksel 0xF16 ; addresses, F16h through F5Fh, are also used by SFRs, but are not part of the access RAM |
165 clrf REFOCON ; no reference oscillator active on REFO pin | |
166 clrf ODCON1 ; disable open drain capability | |
167 clrf ODCON2 ; disable open drain capability | |
168 clrf ODCON3 ; disable open drain capability | |
608 | 169 clrf CM1CON ; disable |
604 | 170 clrf CM2CON ; disable |
171 clrf CM3CON ; disable | |
0 | 172 |
604 | 173 movlw b'11000000' ; ANSEL, AN7 and AN6 -> Analog inputs, PORTA is digital |
0 | 174 movwf ANCON0 |
604 | 175 movlw b'00000111' ; ANSEL, AN8, AN9, AN10 -> Analog input |
0 | 176 movwf ANCON1 |
177 movlw b'00000010' ; ANSEL, AN17 -> Analog input | |
178 movwf ANCON2 | |
604 | 179 banksel common |
0 | 180 |
604 | 181 ; movlw b'00000000' ; 1= input -> Data TFT_high |
448 | 182 clrf TRISA |
604 | 183 ; movlw b'00000000' ; init port |
448 | 184 clrf PORTA |
0 | 185 |
604 | 186 movlw b'00000011' ; 1= input, (RB0, RB1) -> switches, RB2 -> Power_MCP, RB3 -> s8_npower, RB4 -> LED_green/rx_nreset, RB5 -> /TFT_POWER |
0 | 187 movwf TRISB |
604 | 188 movlw b'00111000' ; init port, rx_nreset=1 -> hard reset RX |
0 | 189 movwf PORTB |
190 | |
604 | 191 movlw b'10011010' ; 1= input, (RC0, RC1) -> SOSC, RC2 -> TFT_LED_PWM, (RC3,RC4) -> I²C, RC5 -> MOSI_MS5541, (RC6, RC7) -> UART1 |
0 | 192 movwf TRISC |
604 | 193 ; movlw b'00000000' ; init port |
448 | 194 clrf PORTC |
0 | 195 |
604 | 196 movlw b'00100000' ; 1= input, RD0 -> TFT_NCS, RD1 -> TFT_RS, RD2 -> TFT_NWR, RD3 -> TFT_RD, RD4 -> MOSI_Flash, RD5 -> MISO_Flash, RD6 -> CLK_Flash, RD7 -> TFT_NRESET |
0 | 197 movwf TRISD |
604 | 198 ; movlw b'00000000' ; init port |
448 | 199 clrf PORTD |
0 | 200 |
628 | 201 movlw b'00100000' ; 1= input, RE0 -> not_Power_BLE, RE1 -> Power_IR, RE2 -> CS_MCP, RE3 -> LED_blue, RE4 -> power_sw1, RE5 -> leave as input |
627 | 202 movwf TRISE |
203 movlw b'00010001' ; init port | |
0 | 204 movwf PORTE |
205 | |
604 | 206 movlw b'01111110' ; 1= input, (RF1, RF2, RF3, RF4, RF5) -> Analog |
0 | 207 movwf TRISF |
604 | 208 ; movlw b'00000000' ; init port |
448 | 209 clrf PORTF |
0 | 210 |
628 | 211 movlw b'00001110' ; 1= input, <7:6> not implemented, RG0 -> TX3_PIEZO_CFG, , RG1 -> TX2, RG2 -> RX2, RG3 -> AN17_RSSI, RG4 -> SOSC_OUT, RG5 -> /RESET |
0 | 212 movwf TRISG |
604 | 213 movlw b'00000001' ; init port |
0 | 214 movwf PORTG |
215 | |
604 | 216 ; movlw b'00000000' ; 1= input -> Data TFT_low |
448 | 217 clrf TRISH |
604 | 218 ; movlw b'00000000' ; init port |
448 | 219 clrf PORTH |
0 | 220 |
623 | 221 movlw b'10011011' ; 1= input, RJ4 -> vusb_in, RJ5 -> power_sw2, RJ6 -> CLK_MS5541, RJ7 -> MISO_MS5541 |
0 | 222 movwf TRISJ |
604 | 223 movlw b'00100000' ; init port |
0 | 224 movwf PORTJ |
225 | |
623 | 226 |
618 | 227 ; disable Charger by default |
623 | 228 bsf charge_disable ; set charging-inhibit signal |
229 bcf charge_enable ; activate charging-inhibit signal | |
230 | |
231 | |
0 | 232 ; Timer 0 |
604 | 233 movlw b'00000001' ; timer0 with 1:4 prescaler |
0 | 234 movwf T0CON |
235 | |
623 | 236 |
0 | 237 ; Timer 1 - Button hold-down timer |
623 | 238 movlw b'10001100' ; 32768 Hz clock source, 1:1 prescaler -> ; 30.51757813 µs/bit in TMR1L:TMR1H |
0 | 239 movwf T1CON |
240 | |
623 | 241 |
0 | 242 ; RTCC |
623 | 243 banksel 0xF16 ; addresses, F16h through F5Fh, are also used by SFRs, but are not part of the access RAM |
244 movlw 0x55 | |
245 movwf EECON2 | |
246 movlw 0xAA | |
247 movwf EECON2 | |
248 bsf RTCCFG,RTCWREN ; unlock sequence for RTCWREN | |
0 | 249 bsf RTCCFG,RTCPTR1 |
250 bsf RTCCFG,RTCPTR0 | |
623 | 251 bsf RTCCFG,RTCEN ; module enable |
252 bsf RTCCFG,RTCOE ; output enable | |
253 movlw b'00000100' ; 32768 Hz SOCS on RTCC pin (PORTG,4) Bit7-5: pull-ups for Port D, E and J | |
0 | 254 movwf PADCFG1 |
623 | 255 movlw b'11000000' |
256 movwf ALRMCFG ; 1/2 second alarm | |
0 | 257 movlw d'1' |
623 | 258 movwf ALRMRPT ; alarm repeat counter |
259 movlw 0x55 | |
260 movwf EECON2 | |
261 movlw 0xAA | |
262 movwf EECON2 | |
263 bcf RTCCFG,RTCWREN ; lock sequence for RTCWREN | |
264 banksel common | |
265 | |
614 | 266 |
0 | 267 ; A/D Converter |
268 movlw b'00011000' ; power off ADC, select AN6 | |
269 movwf ADCON0 | |
270 movlw b'00100000' ; 2.048V Vref+ | |
271 movwf ADCON1 | |
604 | 272 movlw b'10001101' ; right aligned |
0 | 273 movwf ADCON2 |
274 | |
623 | 275 |
276 ; serial Port 1 (TRISC6/7) | |
0 | 277 movlw b'00001000' ; BRG16=1 |
278 movwf BAUDCON1 | |
623 | 279 movlw .34 ; SPBRGH:SPBRG = .34 : 114285 BAUD @ 16MHz (+0.79% Error at 115200 BAUD) |
280 movwf SPBRG1 ; SPBRGH:SPBRG = .207 : 19230 BAUD @ 16MHz (-0.16% Error at 19200 BAUD) | |
0 | 281 clrf SPBRGH1 ; |
204 | 282 |
283 clrf RCSTA1 | |
604 | 284 clrf TXSTA1 ; UART disable |
285 bcf PORTC,6 ; TX hard to GND | |
0 | 286 |
623 | 287 |
288 ; serial Port 2 (TRISG2) for IR/S8 digital interface | |
289 ; | |
290 ; - will be initialized by enable_ir_s8 (eeprom_rs232.asm) in case IR/S8 shall be available | |
291 | |
0 | 292 |
623 | 293 ; Timer 3 for IR-RX Timeout |
294 IFDEF _external_sensor | |
604 | 295 clrf T3GCON ; reset Timer3 gate control register |
623 | 296 movlw b'10001001' ; synced, 1:1 prescaler -> 2 seconds till overrun @ 32768 Hz, |
297 ; incrementing by 1 bit each 30.51757813 µs | |
0 | 298 movwf T3CON |
623 | 299 ENDIF |
300 | |
0 | 301 |
302 ; SPI Module(s) | |
303 ; SPI2: External Flash | |
304 movlw b'00110000' | |
305 movwf SSP2CON1 | |
448 | 306 ; movlw b'00000000' |
307 clrf SSP2STAT | |
623 | 308 ; -> 0.25 MHz Bit clock @ 1 MHz mode (Eco) |
309 ; -> 4 MHz Bit clock @ 16 MHz mode (Normal) | |
310 ; -> 16 MHz Bit clock @ 64 MHz mode (Fastest) | |
311 | |
0 | 312 |
313 ; MSSP1 Module: I2C Master | |
604 | 314 movlw b'00101000' ; I2C master mode |
0 | 315 movwf SSP1CON1 |
448 | 316 ; movlw b'00000000' |
317 clrf SSP1CON2 | |
628 | 318 movlw 0x9C |
319 movwf SSP1ADD ; 100kHz @ 64MHz Fosc | |
0 | 320 |
623 | 321 |
0 | 322 ; PWM Module(s) |
623 | 323 ; PWM 1 for LED dimming |
0 | 324 movlw b'00001100' |
325 movwf CCP1CON | |
326 movlw b'00000001' | |
604 | 327 movwf PSTR1CON ; pulse steering disabled |
628 | 328 movlw d'254' |
604 | 329 movwf PR2 ; period |
330 ; 255 is max brightness (300 mW) | |
331 clrf CCPR1L ; duty cycle | |
332 clrf CCPR1H ; duty cycle | |
0 | 333 movlw T2CON_NORMAL |
334 movwf T2CON | |
335 | |
623 | 336 |
337 ; Timer 5 for ISR-independent wait routines | |
604 | 338 clrf T5GCON ; reset Timer5 gate control register |
623 | 339 movlw b'10001011' ; synced, 16 bit mode, 1:1 prescaler -> 2 seconds till overrun @ 32768 Hz, |
340 movwf T5CON ; incrementing by 1 bit each 30.51757813 µs | |
341 | |
0 | 342 |
623 | 343 banksel 0xF16 ; addresses F16h through F5Fh are also used by SFRs, but are not part of the access RAM |
344 | |
345 ; Timer 7 for 62.5 ms Interrupt (sensor states) | |
604 | 346 clrf T7GCON ; reset Timer7 gate control register |
623 | 347 movlw b'10001001' ; 1:1 prescaler -> 2 seconds @ 32768 Hz, synced |
0 | 348 movwf T7CON |
349 clrf TMR7L | |
350 movlw .248 | |
623 | 351 movwf TMR7H ; -> rollover after 2048 cycles -> 62.5 ms |
0 | 352 |
623 | 353 |
354 ; turn off unused timers | |
608 | 355 movlw b'11000000' |
356 movwf PMD0 | |
623 | 357 IFDEF _external_sensor |
608 | 358 movlw b'11010001' |
623 | 359 ELSE |
360 movlw b'11011001' ; includes turning off timer 3 | |
361 ENDIF | |
608 | 362 movwf PMD1 |
363 movlw b'11010111' | |
364 movwf PMD2 | |
365 movlw b'11111111' | |
366 movwf PMD3 | |
367 | |
623 | 368 |
369 ; turn off unused CTMU | |
608 | 370 clrf CTMUCONH |
371 clrf CTMUCONL | |
372 clrf CTMUICON | |
623 | 373 |
604 | 374 banksel common |
608 | 375 |
623 | 376 |
0 | 377 ; Interrupts |
50 | 378 movlw b'11010000' |
0 | 379 movwf INTCON |
623 | 380 movlw b'00001000' ; Bit7=1: pull-up for PORTB disabled |
0 | 381 movwf INTCON2 |
77
131e6dd9e201
BUGFIX: Potential bug to freeze the OSTC3 after battery change or update
heinrichsweikamp
parents:
58
diff
changeset
|
382 movlw b'00000000' |
0 | 383 movwf INTCON3 |
384 movlw b'00000001' ; Bit0: TMR1 | |
385 movwf PIE1 | |
386 movlw b'00000010' ; Bit1: TMR3 | |
604 | 387 movwf PIE2 |
0 | 388 movlw b'00000000' ; Bit1: TMR5 |
389 movwf PIE5 | |
390 movlw b'00100001' ; Bit0: RTCC, Bit5: UART2 | |
391 movwf PIE3 | |
392 movlw b'00001000' ; Bit3: TMR7 | |
393 movwf PIE5 | |
394 | |
623 | 395 bcf active_reset_ostc_rx ; release RESET from RX circuitry |
396 ;bra power_up_switches | |
397 | |
0 | 398 |
623 | 399 global power_up_switches |
400 power_up_switches: | |
401 bsf power_sw1 ; switch on power supply for switch 1 | |
402 btfss power_sw1 ; power established? | |
403 bra $-4 ; NO - wait | |
404 bsf power_sw2 ; switch on power supply for switch 2 | |
405 btfss power_sw2 ; power established? | |
406 bra $-4 ; NO - wait | |
204 | 407 |
0 | 408 return |
409 | |
410 ;============================================================================= | |
623 | 411 ; CPU speed change request functions |
412 | |
413 global request_speed_eco | |
414 request_speed_eco: | |
415 movlw coding_speed_eco ; load coding for eco speed | |
416 movwf cpu_speed_request ; request ISR to change the CPU speed | |
417 return ; done | |
418 | |
419 global request_speed_normal | |
420 request_speed_normal: | |
421 movlw coding_speed_normal ; load coding for normal speed | |
422 movwf cpu_speed_request ; request ISR to change the CPU speed | |
423 return ; done | |
424 | |
425 global request_speed_fastest | |
426 request_speed_fastest: | |
427 movlw coding_speed_fastest ; load coding for fastest speed | |
428 movwf cpu_speed_request ; request ISR to change the CPU speed | |
429 return ; done | |
430 | |
0 | 431 ;============================================================================= |
623 | 432 ; Backup the first 128 bytes from program memory to EEPROM |
433 ; | |
434 global backup_flash_page | |
435 backup_flash_page: | |
436 banksel common | |
437 movlw 0x00 ; start address in internal program memory | |
438 movwf TBLPTRL | |
439 movwf TBLPTRH | |
440 movwf TBLPTRU | |
441 | |
442 movlw .128 ; copy 1 block = 128 byte | |
443 movwf lo ; byte counter | |
444 | |
445 clrf EEADR ; start address in EEPROM, low | |
446 movlw .3 ; start address in EEPROM, high | |
447 movwf EEADRH | |
448 | |
449 TBLRD*- ; dummy read to be in 128 byte block | |
450 backup_flash_loop: | |
451 tblrd+* ; read one byte from program memory (with pre-increment) | |
452 movff TABLAT,EEDATA ; transfer byte from program memory read to EEPROM write | |
453 call write_eeprom ; execute EEPROM write | |
454 incf EEADR,F ; increment EEPROM address | |
455 decfsz lo,F ; 128 byte done? | |
456 bra backup_flash_loop ; NO - loop | |
457 clrf EEADRH ; YES - reset EEPROM high address | |
458 return ; - done | |
459 | |
0 | 460 ;============================================================================= |
623 | 461 ; Restore the first 128 bytes from EEPROM to program memory |
462 ; | |
463 global restore_flash | |
464 restore_flash: | |
465 banksel common | |
466 movlw 0x00 ; start address in internal program memory | |
467 movwf TBLPTRL | |
468 movwf TBLPTRH | |
469 movwf TBLPTRU | |
470 | |
471 movlw b'10010100' ; setup block erase | |
472 rcall restore_write ; execute block erase | |
473 | |
474 movlw .128 ; copy 1 block = 128 byte | |
475 movwf lo ; byte counter | |
476 | |
477 clrf EEADR ; start address in EEPROM, low | |
478 movlw .3 ; start address in EEPROM, high | |
479 movwf EEADRH | |
480 | |
481 TBLRD*- ; dummy read to be in 128 byte block | |
482 restore_flash_loop: | |
483 call read_eeprom ; read one byte from EEPROM | |
484 incf EEADR,F ; increment EEPROM address | |
485 movff EEDATA,TABLAT ; transfer byte from EEPROM read to program memory write | |
486 tblwt+* ; execute program memory write (with pre-increment) | |
487 decfsz lo,F ; 128 bytes done? | |
488 bra restore_flash_loop ; NO - loop | |
489 movlw b'10000100' ; YES - setup block write | |
490 rcall restore_write ; - execute block write | |
491 reset ; - done, reset CPU | |
492 | |
493 restore_write: | |
494 movwf EECON1 ; type of memory to write in | |
495 movlw 0x55 | |
496 movwf EECON2 | |
497 movlw 0xAA | |
498 movwf EECON2 | |
499 bsf EECON1,WR ; execute write | |
500 nop | |
501 nop | |
0 | 502 return |
503 | |
604 | 504 END |