1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
11 * Copyright (C) 2003-2005 Jörg Hohensohn
13 * Alpine CD changer Project
14 * This is a feasibility study for Archos emulating an Alpine M-Bus CD changer.
16 * Currently it will do seeks and change tracks, but nothing like disks.
17 * The debug version shows a dump of the M-Bus communication on screen.
19 * Usage: Start plugin, it will stay in the background and do the emulation.
20 * You need to make an adapter with an 8-pin DIN plug for the radio at one end
21 * and a 4-ring 3.5 mm plug for the Archos headphone jack at the other.
22 * The Archos remote pin connects to the M-Bus, audio as usual.
24 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
32 ****************************************************************************/
36 /* Only build for (correct) target */
37 #if CONFIG_CPU==SH7034 && !(CONFIG_STORAGE & STORAGE_MMC)
41 #ifdef HAVE_LCD_CHARCELLS /* player model */
44 #else /* recorder models */
46 #define COLUMNS 32 /* can't really tell for proportional font */
49 /****************** imports ******************/
54 /****************** constants ******************/
56 /* measured bit time on the M-Bus is 3.075 ms = 325.2 Hz */
57 #define MBUS_BAUDRATE 3252 /* make it 10 * bittime */
58 #define MBUS_STEP_FREQ (MBUS_BAUDRATE/2) /* 5 steps per bit */
59 #define MBUS_BIT_FREQ (MBUS_BAUDRATE/10) /* the true bit frequency again */
61 #define MBUS_MAX_SIZE 16 /* maximum length of an M-Bus packet, incl. checksum */
62 #define MBUS_RCV_QUEUESIZE 4 /* how many packets can be queued by receiver */
64 #define ERI1 (*((volatile unsigned long*)0x090001A0)) /* RX error */
65 #define RXI1 (*((volatile unsigned long*)0x090001A4)) /* RX */
70 #define RX_BUSY 0 /* reception in progress */
71 #define RX_RECEIVED 1 /* valid data available */
72 #define RX_FRAMING 2 /* frame error */
73 #define RX_OVERRUN 3 /* receiver overrun */
74 #define RX_PARITY 4 /* parity error */
75 #define RX_SYMBOL 5 /* invalid bit timing */
76 #define RX_OVERFLOW 6 /* buffer full */
77 #define RX_OVERLAP 7 /* receive interrupt while transmitting */
79 /* timer operation mode */
80 #define TM_OFF 0 /* not in use */
81 #define TM_TRANSMIT 1 /* periodic timer to transmit */
82 #define TM_RX_TIMEOUT 2 /* single shot for receive timeout */
84 /* emulation play state */
86 #define EMU_PREPARING 1
95 /****************** prototypes ******************/
97 void timer_init(unsigned hz
, unsigned to
); /* setup static timer registers and values */
98 void timer_set_mode(int mode
); /* define for what the timer should be used right now */
99 void timer4_isr(void); /* IMIA4 ISR */
101 void transmit_isr(void); /* 2nd level ISR for M-Bus transmission */
103 void uart_init(unsigned baudrate
); /* UART setup */
104 void uart_rx_isr(void) __attribute__((interrupt_handler
)); /* RXI1 ISR */
105 void uart_err_isr(void) __attribute__((interrupt_handler
)); /* ERI1 ISR */
106 void receive_timeout_isr(void); /* 2nd level ISR for receiver timeout */
108 void mbus_init(void); /* prepare the M-Bus layer */
109 int mbus_send(unsigned char* p_msg
, int digits
); /* packet send */
110 int mbus_receive(unsigned char* p_msg
, unsigned bufsize
, int timeout
); /* packet receive */
112 unsigned char calc_checksum(unsigned char* p_msg
, int digits
); /* make M-Bus checksum */
113 bool bit_test(unsigned char* buf
, unsigned bit
); /* test one bit of M-Bus packet */
114 void bit_set(unsigned char* buf
, unsigned bit
, bool val
); /* set/clear one bit of M-Bus packet */
116 void print_scroll(char* string
); /* implements a scrolling screen */
117 void dump_packet(char* dest
, int dst_size
, char* src
, int n
); /* convert packet to ASCII */
119 void emu_init(void); /* init changer emulation */
120 void emu_process_packet(unsigned char* mbus_msg
, int msg_size
); /* feed a received radio packet */
121 void emu_tick(void); /* for regular actions of the emulator */
123 int get_playtime(void); /* return the current track time in seconds */
124 int get_tracklength(void); /* return the total length of the current track */
125 void set_track(int selected
);
126 int get_track(void); /* return the track number */
127 void set_play(void); /* start or resume playback */
128 void set_pause(void); /* pause playback */
129 void set_stop(void); /* stop playback */
130 void set_position(int seconds
); /* seek */
131 void get_playmsg(void); /* update the play message with Rockbox info */
132 void get_diskmsg(void); /* update the disk status message with Rockbox info */
134 void sound_neutral(void); /* set to everything flat and 0 dB volume */
135 void sound_normal(void); /* return to user settings */
137 void thread(void); /* the thread running it all */
138 int main(const void* parameter
); /* main loop */
139 enum plugin_status
plugin_start(const void* parameter
); /* entry */
142 /****************** data types ******************/
144 /* one entry in the receive queue */
147 unsigned char buf
[MBUS_MAX_SIZE
]; /* message buffer */
148 unsigned size
; /* length of data in the buffer */
149 unsigned error
; /* error code from reception */
153 /****************** globals ******************/
156 /* information owned by the timer transmit ISR */
159 unsigned char send_buf
[MBUS_MAX_SIZE
]; /* M-Bus message */
160 unsigned send_size
; /* current length of data in the buffer */
161 unsigned index
; /* index for which byte to send */
162 unsigned byte
; /* which byte to send */
163 unsigned bitmask
; /* which bit to send */
164 unsigned step
; /* where in the pulse are we */
165 bool bit
; /* currently sent bit */
166 bool collision
; /* set if a collision happened */
167 bool busy
; /* flag if in transmission */
171 /* information owned by the UART receive ISR */
174 t_rcv_queue_entry queue
[MBUS_RCV_QUEUESIZE
]; /* M-Bus message queue */
175 unsigned buf_read
; /* readout maintained by the user application */
176 unsigned buf_write
; /* writing maintained by ISR */
177 bool overflow
; /* indicate queue overflow */
178 unsigned byte
; /* currently assembled byte */
179 unsigned bit
; /* which bit to receive */
183 /* information owned by the timer */
186 unsigned mode
; /* off, transmit, receive timout */
187 unsigned transmit
; /* value for transmit */
188 unsigned timeout
; /* value for receive timeout */
192 /* information owned by the changer emulation */
195 unsigned char playmsg
[15]; /* current play state msg */
196 unsigned char changemsg
[11]; /* changing message */
197 unsigned char diskmsg
[12]; /* disk status message */
198 long poll_interval
; /* call the emu each n ticks */
199 int time
; /* seconds within the song */
200 int set_state
; /* the desired state to change into */
204 /* communication to the worker thread */
207 bool foreground
; /* set as long as we're owning the UI */
208 bool exiting
; /* signal to the thread that we want to exit */
209 unsigned int thread
; /* worker thread id */
213 /****************** implementation ******************/
216 /* setup static timer registers and values */
217 void timer_init(unsigned hz
, unsigned to
)
219 rb
->memset(&gTimer
, 0, sizeof(gTimer
));
221 gTimer
.transmit
= TIMER_FREQ
/ hz
; /* time for bit transitions */
222 gTimer
.timeout
= TIMER_FREQ
/ to
; /* time for receive timeout */
226 /* define for what the timer should be used right now */
227 void timer_set_mode(int mode
)
229 TCNT4
= 0; /* start counting at 0 */
230 gTimer
.mode
= mode
; /* store the mode */
232 if (mode
== TM_RX_TIMEOUT
)
234 rb
->timer_register(1, NULL
, gTimer
.timeout
, timer4_isr
IF_COP(, CPU
));
235 IPRD
= (IPRD
& 0xFF0F) | 11 << 4; /* interrupt priority */
237 else if (mode
== TM_TRANSMIT
)
239 rb
->timer_register(1, NULL
, gTimer
.transmit
, timer4_isr
IF_COP(, CPU
));
240 IPRD
= (IPRD
& 0xFF0F) | 14 << 4; /* interrupt priority */
244 rb
->timer_unregister();
249 void timer4_isr(void) /* IMIA4 */
252 { /* distribute the interrupt */
257 receive_timeout_isr();
258 rb
->timer_unregister(); /* single shot */
261 timer_set_mode(TM_OFF
); /* spurious interrupt */
266 /* About Alpine M-Bus
269 * The protocol uses a single wire in half duplex mode.
270 * A bit like I2C, this wire is either pulled low or left floating high.
271 * Bit time is ~3 ms, a "zero" is coded as ~0.6 ms low, a "one" as ~1.8 ms low.
272 * Nice to view in a 0.6 ms grid:
274 * 0 0.6 1.2 1.8 2.4 3.0
276 * __ ___________________
277 * \____/ \ "zero" bit
279 * \______________/ \ "one" bit
281 * So I send out the data in a timer interrupt spawned to 0.6 ms.
282 * In phases where the line is floating high, I can check for collisions.
283 * (happens if the other side driving it low, too.)
285 * Data is transmitted in multiples of 4 bit, to ease BCD representation.
289 /* 2nd level ISR for M-Bus transmission */
290 void transmit_isr(void)
294 TSR4
&= ~0x01; /* clear the interrupt */
296 switch(gSendIRQ
.step
++)
299 and_b(~0x04, &PBDRH
); /* low (read-modify-write access may have changed it while it was input) */
300 or_b(0x04, &PBIORH
); /* drive low (output) */
303 if (!gSendIRQ
.bit
) /* sending "zero"? */
304 and_b(~0x04, &PBIORH
); /* float (input) */
307 if (!gSendIRQ
.bit
&& ((PBDR
& PB10
) == 0))
308 gSendIRQ
.collision
= true;
311 if (gSendIRQ
.bit
) /* sending "one"? */
312 and_b(~0x04, &PBIORH
); /* float (input) */
313 else if ((PBDR
& PB10
) == 0)
314 gSendIRQ
.collision
= true;
317 if ((PBDR
& PB10
) == 0)
318 gSendIRQ
.collision
= true;
320 /* prepare next round */
322 gSendIRQ
.bitmask
>>= 1;
323 if (gSendIRQ
.bitmask
)
325 gSendIRQ
.bit
= (gSendIRQ
.byte
& gSendIRQ
.bitmask
) != 0;
329 if (++gSendIRQ
.index
< gSendIRQ
.send_size
)
331 gSendIRQ
.bitmask
= 0x08;
332 gSendIRQ
.byte
= gSendIRQ
.send_buf
[gSendIRQ
.index
];
333 gSendIRQ
.bit
= (gSendIRQ
.byte
& gSendIRQ
.bitmask
) != 0;
336 exit
= true; /* done */
341 if (exit
|| gSendIRQ
.collision
)
342 { /* stop transmission */
343 or_b(0x20, PBCR1_ADDR
+1); /* RxD1 again for PB10 */
344 timer_set_mode(TM_OFF
); /* stop the timer */
345 gSendIRQ
.busy
= false; /* do this last, to avoid race conditions */
350 /* For receiving, I use the "normal" serial RX feature of the CPU,
351 * so we can receive within an interrupt, no line polling necessary.
352 * Luckily, the M-Bus bit always starts with a falling edge and ends with a high,
353 * this matches with the start bit and the stop bit of a serial transmission.
354 * The baudrate is set such that the M-Bus bit time (ca. 3ms) matches
355 * the serial reception time of one byte, so we receive one byte per
357 * Start bit, 8 data bits and stop bit (total=10) nicely fall into the 5
360 * 0 0.6 1.2 1.8 2.4 3.0 ms
362 * __ _______________________________
363 * \_______/ \ "zero" bit
365 * \_______________________/ \ "one" bit
367 * | | | | | | | | | | | serial sampling interval
368 * Start 0 1 2 3 4 5 6 7 Stop bit (LSB first!)
370 * By looking at the bit pattern in the serial byte we can distinguish
371 * the short low from the longer low, tell "zero" and "one" apart.
372 * So we receive 0xFE for a "zero", 0xE0 for a "one".
373 * It may be necessary to treat the bits next to transitions as don't care,
374 * in case the timing is not so accurate.
375 * Bits are always sent "back-to-back", so I detect the packet end by timeout.
379 void uart_init(unsigned baudrate
)
381 RXI1
= (unsigned long)uart_rx_isr
; /* install ISR */
382 ERI1
= (unsigned long)uart_err_isr
; /* install ISR */
384 SCR1
= 0x00; /* disable everything; select async mode with SCK pin as I/O */
385 SMR1
= 0x00; /* async, 8N1, NoMultiProc, sysclock/1 */
386 BRR1
= ((FREQ
/(32*baudrate
))-1);
388 IPRE
= (IPRE
& ~0xf000) | 0xc000; /* interrupt on level 12 */
390 rb
->sleep(1); /* hardware needs to settle for at least one bit interval */
392 and_b(~(SCI_RDRF
| SCI_ORER
| SCI_FER
| SCI_PER
), &SSR1
); /* clear any receiver flag */
393 or_b(SCI_RE
| SCI_RIE
, &SCR1
); /* enable the receiver with interrupt */
397 void uart_rx_isr(void) /* RXI1 */
400 t_rcv_queue_entry
* p_entry
= &gRcvIRQ
.queue
[gRcvIRQ
.buf_write
]; /* short cut */
402 data
= RDR1
; /* get data */
404 and_b(~SCI_RDRF
, &SSR1
); /* clear data received flag */
406 if (gTimer
.mode
== TM_TRANSMIT
)
407 p_entry
->error
= RX_OVERLAP
; /* oops, we're also transmitting, stop */
409 timer_set_mode(TM_RX_TIMEOUT
); /* (re)spawn timeout */
411 if (p_entry
->error
!= RX_BUSY
)
414 if ((data
& ~0x00) == 0xFE) /* 01111111 in line order (reverse) */
415 { /* "zero" received */
418 else if ((data
& ~0x00) == 0xE0) /* 00000111 in line order (reverse) */
419 { /* "one" received */
420 gRcvIRQ
.byte
= gRcvIRQ
.byte
<< 1 | 0x01;
423 { /* unrecognized pulse */
424 p_entry
->error
= RX_SYMBOL
;
427 if (p_entry
->error
== RX_BUSY
)
429 if (++gRcvIRQ
.bit
>= 4)
430 { /* byte completed */
431 if (p_entry
->size
>= sizeof(p_entry
->buf
))
433 p_entry
->error
= RX_OVERFLOW
; /* buffer full */
437 p_entry
->buf
[p_entry
->size
] = gRcvIRQ
.byte
;
447 void uart_err_isr(void) /* ERI1 */
449 t_rcv_queue_entry
* p_entry
= &gRcvIRQ
.queue
[gRcvIRQ
.buf_write
]; /* short cut */
451 if (p_entry
->error
== RX_BUSY
)
452 { /* terminate reception in case of error */
454 p_entry
->error
= RX_FRAMING
;
455 else if (SSR1
& SCI_ORER
)
456 p_entry
->error
= RX_OVERRUN
;
457 else if (SSR1
& SCI_PER
)
458 p_entry
->error
= RX_PARITY
;
461 /* clear any receiver flag */
462 and_b(~(SCI_RDRF
| SCI_ORER
| SCI_FER
| SCI_PER
), &SSR1
);
466 /* 2nd level ISR for receiver timeout, this finalizes reception */
467 void receive_timeout_isr(void)
469 t_rcv_queue_entry
* p_entry
= &gRcvIRQ
.queue
[gRcvIRQ
.buf_write
]; /* short cut */
471 timer_set_mode(TM_OFF
); /* single shot */
473 if (p_entry
->error
== RX_BUSY
) /* everthing OK so far? */
474 p_entry
->error
= RX_RECEIVED
; /* end with valid data */
476 /* move to next queue entry */
478 if (gRcvIRQ
.buf_write
>= MBUS_RCV_QUEUESIZE
)
479 gRcvIRQ
.buf_write
= 0;
480 p_entry
= &gRcvIRQ
.queue
[gRcvIRQ
.buf_write
];
482 if (gRcvIRQ
.buf_write
== gRcvIRQ
.buf_read
)
483 { /* queue overflow */
484 gRcvIRQ
.overflow
= true;
485 /* what can I do? Continueing overwrites the oldest. */
491 p_entry
->error
= RX_BUSY
; /* enable receive on new entry */
495 /* generate the checksum */
496 unsigned char calc_checksum(unsigned char* p_msg
, int digits
)
501 for (i
=0; i
<digits
; i
++)
511 /****************** high-level M-Bus functions ******************/
515 /* init the send object */
516 rb
->memset(&gSendIRQ
, 0, sizeof(gSendIRQ
));
517 timer_init(MBUS_STEP_FREQ
, (MBUS_BIT_FREQ
*10)/15); /* setup frequency and timeout (1.5 bit) */
520 rb
->memset(&gRcvIRQ
, 0, sizeof(gRcvIRQ
));
521 uart_init(MBUS_BAUDRATE
);
525 /* send out a number of BCD digits (one per byte) with M-Bus protocol */
526 int mbus_send(unsigned char* p_msg
, int digits
)
528 /* wait for previous transmit/receive to end */
529 while(gTimer
.mode
!= TM_OFF
) /* wait for "free line" */
532 /* fill in our part */
533 rb
->memcpy(gSendIRQ
.send_buf
, p_msg
, digits
);
536 gSendIRQ
.send_buf
[digits
] = calc_checksum(p_msg
, digits
);
539 /* debug dump, to be removed */
540 if (gTread
.foreground
)
542 char buf
[MBUS_MAX_SIZE
+1];
543 dump_packet(buf
, sizeof(buf
), gSendIRQ
.send_buf
, digits
);
544 /*print_scroll(buf); */
547 gSendIRQ
.send_size
= digits
;
549 /* prepare everything so the ISR can start right away */
551 gSendIRQ
.byte
= gSendIRQ
.send_buf
[0];
552 gSendIRQ
.bitmask
= 0x08;
554 gSendIRQ
.bit
= (gSendIRQ
.byte
& gSendIRQ
.bitmask
) != 0;
555 gSendIRQ
.collision
= false;
556 gSendIRQ
.busy
= true;
558 /* last chance to wait for a new detected receive to end */
559 while(gTimer
.mode
!= TM_OFF
) /* wait for "free line" */
562 and_b(~0x30, PBCR1_ADDR
+1); /* GPIO for PB10 */
563 timer_set_mode(TM_TRANSMIT
); /* run */
565 /* make the call blocking until sent out */
566 rb
->sleep(digits
*4*HZ
/MBUS_BIT_FREQ
); /* should take this long */
568 while(gSendIRQ
.busy
) /* poll in case it lasts longer */
569 rb
->sleep(1); /* (should not happen) */
571 /* debug output, to be removed */
572 if (gTread
.foreground
)
574 if (gSendIRQ
.collision
)
575 print_scroll("collision");
578 return gSendIRQ
.collision
;
582 /* returns the size of message copy, 0 if timed out, negative on error */
583 int mbus_receive(unsigned char* p_msg
, unsigned bufsize
, int timeout
)
589 if (gRcvIRQ
.buf_read
!= gRcvIRQ
.buf_write
)
590 { /* something in the queue */
591 t_rcv_queue_entry
* p_entry
= &gRcvIRQ
.queue
[gRcvIRQ
.buf_read
]; /* short cut */
593 if (p_entry
->error
== RX_RECEIVED
)
595 rb
->memcpy(p_msg
, p_entry
->buf
, MIN(p_entry
->size
, bufsize
));
596 retval
= p_entry
->size
; /* return message size */
599 { /* an error occured */
600 retval
= - p_entry
->error
; /* return negative number */
603 /* next queue readout position */
605 if (gRcvIRQ
.buf_read
>= MBUS_RCV_QUEUESIZE
)
606 gRcvIRQ
.buf_read
= 0;
608 return retval
; /* exit */
611 if (timeout
!= 0 || gTimer
.mode
!= TM_OFF
) /* also carry on if reception in progress */
613 if (timeout
!= -1 && timeout
!= 0) /* if not infinite or expired */
616 rb
->sleep(1); /* wait a while */
619 } while (timeout
!= 0 || gTimer
.mode
!= TM_OFF
);
621 return 0; /* timeout */
625 /****************** MMI helper fuctions ******************/
628 void print_scroll(char* string
)
630 static char screen
[LINES
][COLUMNS
+1]; /* visible strings */
631 static unsigned pos
= 0; /* next print position */
632 static unsigned screentop
= 0; /* for scrolling */
634 if (!gTread
.foreground
)
635 return; /* just to protect careless callers */
638 { /* need to scroll first */
640 rb
->lcd_clear_display();
642 for (i
=0; i
<LINES
-1; i
++)
643 rb
->lcd_puts(0, i
, screen
[(i
+screentop
) % LINES
]);
648 /* no strncpy avail. */
649 rb
->snprintf(screen
[(pos
+screentop
) % LINES
], sizeof(screen
[0]), "%s", string
);
651 rb
->lcd_puts(0, pos
, screen
[(pos
+screentop
) % LINES
]);
657 void dump_packet(char* dest
, int dst_size
, char* src
, int n
)
660 int len
= MIN(dst_size
-1, n
);
662 for (i
=0; i
<len
; i
++)
663 { /* convert to hex digits */
664 dest
[i
] = src
[i
] < 10 ? '0' + src
[i
] : 'A' + src
[i
] - 10;
666 dest
[i
] = '\0'; /* zero terminate string */
670 /****************** CD changer emulation ******************/
672 bool bit_test(unsigned char* buf
, unsigned bit
)
674 return (buf
[bit
>>2] & BIT_N(bit
&3)) != 0;
678 void bit_set(unsigned char* buf
, unsigned bit
, bool val
)
681 buf
[bit
>>2] |= BIT_N(bit
&3);
683 buf
[bit
>>2] &= ~BIT_N(bit
&3);
689 rb
->memset(&gEmu
, 0, sizeof(gEmu
));
691 gEmu
.poll_interval
= HZ
;
693 /* init the play message to 990000000000000 */
694 gEmu
.playmsg
[0] = gEmu
.playmsg
[1] = 0x9;
696 /* init the changing message to 9B900000001 */
697 gEmu
.changemsg
[0] = gEmu
.changemsg
[2] = 0x9;
698 gEmu
.changemsg
[1] = 0xB;
699 gEmu
.changemsg
[10] = 0x1;
701 /* init the disk status message to 9C1019999990 */
702 rb
->memset(&gEmu
.diskmsg
, 0x9, sizeof(gEmu
.diskmsg
));
703 gEmu
.diskmsg
[1] = 0xC;
704 gEmu
.diskmsg
[2] = gEmu
.diskmsg
[4] = 0x1;
705 gEmu
.diskmsg
[3] = gEmu
.diskmsg
[11] = 0x0;
708 /* feed a radio command into the emulator */
709 void emu_process_packet(unsigned char* mbus_msg
, int msg_size
)
711 bool playmsg_dirty
= false;
712 bool diskmsg_dirty
= false;
714 if (msg_size
== 2 && mbus_msg
[0] == 1 && mbus_msg
[1] == 8)
716 mbus_send("\x09\x08", 2); /* 98: ping OK */
718 else if (msg_size
== 5 && mbus_msg
[0] == 1 && mbus_msg
[1] == 1 && mbus_msg
[2] == 1)
719 { /* set play state */
720 if (bit_test(mbus_msg
, 16))
722 if (gEmu
.set_state
== EMU_FF
|| gEmu
.set_state
== EMU_FR
) /* was seeking? */
723 { /* seek to final position */
724 set_position(gEmu
.time
);
726 else if (gEmu
.set_state
!= EMU_PLAYING
|| gEmu
.set_state
!= EMU_PAUSED
)
727 { /* was not playing yet, better send disk message */
728 diskmsg_dirty
= true;
731 gEmu
.set_state
= EMU_PLAYING
;
732 playmsg_dirty
= true;
735 if (bit_test(mbus_msg
, 17))
737 gEmu
.set_state
= EMU_PAUSED
;
738 playmsg_dirty
= true;
742 if (bit_test(mbus_msg
, 14))
744 gEmu
.set_state
= EMU_STOPPED
;
745 playmsg_dirty
= true;
749 if (bit_test(mbus_msg
, 18))
751 gEmu
.set_state
= EMU_FF
;
752 playmsg_dirty
= true;
756 if (bit_test(mbus_msg
, 19))
758 gEmu
.set_state
= EMU_FR
;
759 playmsg_dirty
= true;
763 if (bit_test(mbus_msg
, 12)) /* scan stop */
765 bit_set(gEmu
.playmsg
, 51, false);
766 playmsg_dirty
= true;
769 if (gEmu
.set_state
== EMU_FF
|| gEmu
.set_state
== EMU_FR
)
770 gEmu
.poll_interval
= HZ
/4; /* faster refresh */
772 gEmu
.poll_interval
= HZ
;
774 else if (msg_size
== 8 && mbus_msg
[0] == 1 && mbus_msg
[1] == 1 && mbus_msg
[2] == 4)
775 { /* set program mode */
776 gEmu
.playmsg
[11] = mbus_msg
[3]; /* copy repeat, random, intro */
777 gEmu
.playmsg
[12] = mbus_msg
[4]; /* ToDo */
778 playmsg_dirty
= true;
780 else if (msg_size
==8 && mbus_msg
[0] == 1 && mbus_msg
[1] == 1 && mbus_msg
[2] == 3)
782 gEmu
.time
= 0; /* reset playtime */
783 playmsg_dirty
= true;
784 if (mbus_msg
[3] == 0)
785 { /* changing track */
786 if (mbus_msg
[4] == 0xA && mbus_msg
[5] == 0x3)
788 gEmu
.playmsg
[3] = rb
->rand() % 10; /* ToDo */
789 gEmu
.playmsg
[4] = rb
->rand() % 10;
791 else if (mbus_msg
[4] == 0xB && mbus_msg
[5] == 0x3)
792 { /* previous random */
793 gEmu
.playmsg
[3] = rb
->rand() % 10; /* ToDo */
794 gEmu
.playmsg
[4] = rb
->rand() % 10;
797 { /* normal track select */
798 set_track(mbus_msg
[4]*10 + mbus_msg
[5]);
802 { /* changing disk */
803 diskmsg_dirty
= true;
804 gEmu
.changemsg
[3] = mbus_msg
[3]; /* copy disk */
805 gEmu
.diskmsg
[2] = mbus_msg
[3];
806 gEmu
.changemsg
[7] = gEmu
.playmsg
[11]; /* copy flags from status */
807 gEmu
.changemsg
[8] = gEmu
.playmsg
[12];
808 /*gEmu.playmsg[3] = 0; */ /* reset to track 1 */
809 /*gEmu.playmsg[4] = 1; */
810 mbus_send(gEmu
.changemsg
, sizeof(gEmu
.changemsg
));
814 { /* if in doubt, send Ack */
815 mbus_send("\x09\x0F\x00\x00\x00\x00\x00", 7);
820 rb
->yield(); /* give the audio thread a chance to process */
821 get_playmsg(); /* force update */
822 mbus_send(gEmu
.playmsg
, sizeof(gEmu
.playmsg
));
827 get_diskmsg(); /* force update */
828 mbus_send(gEmu
.diskmsg
, sizeof(gEmu
.diskmsg
));
833 /* called each second in case the emulator has something to do */
836 get_playmsg(); /* force update */
837 if (bit_test(gEmu
.playmsg
, 56)) /* play bit */
839 unsigned remain
; /* helper as we walk down the digits */
841 switch(gEmu
.set_state
)
850 else if (gEmu
.time
> get_tracklength())
851 gEmu
.time
= get_tracklength();
853 /* convert value to MM:SS */
854 remain
= (unsigned)gEmu
.time
;
855 gEmu
.playmsg
[7] = remain
/ (10*60);
856 remain
-= gEmu
.playmsg
[7] * (10*60);
857 gEmu
.playmsg
[8] = remain
/ 60;
858 remain
-= gEmu
.playmsg
[8] * 60;
859 gEmu
.playmsg
[9] = remain
/ 10;
860 remain
-= gEmu
.playmsg
[9] * 10;
861 gEmu
.playmsg
[10] = remain
;
864 mbus_send(gEmu
.playmsg
, sizeof(gEmu
.playmsg
));
869 /****************** communication with Rockbox playback ******************/
872 /* update the play message with Rockbox info */
873 void get_playmsg(void)
877 if (gEmu
.set_state
!= EMU_FF
&& gEmu
.set_state
!= EMU_FR
)
879 switch(rb
->audio_status())
881 case AUDIO_STATUS_PLAY
:
882 print_scroll("AudioStat Play");
883 if (gEmu
.set_state
== EMU_FF
|| gEmu
.set_state
== EMU_FR
)
884 gEmu
.playmsg
[2] = gEmu
.set_state
; /* set FF/FR */
886 gEmu
.playmsg
[2] = EMU_PLAYING
; /* set normal play */
888 bit_set(gEmu
.playmsg
, 56, true); /* set play */
889 bit_set(gEmu
.playmsg
, 57, false); /* clear pause */
890 bit_set(gEmu
.playmsg
, 59, false); /* clear stop */
893 case AUDIO_STATUS_PLAY
| AUDIO_STATUS_PAUSE
:
894 print_scroll("AudioStat Pause");
895 gEmu
.playmsg
[2] = EMU_PAUSED
;
896 bit_set(gEmu
.playmsg
, 56, false); /* clear play */
897 bit_set(gEmu
.playmsg
, 57, true); /* set pause */
898 bit_set(gEmu
.playmsg
, 59, false); /* clear stop */
902 print_scroll("AudioStat 0");
903 gEmu
.playmsg
[2] = EMU_STOPPED
;
904 bit_set(gEmu
.playmsg
, 56, false); /* clear play */
905 bit_set(gEmu
.playmsg
, 57, false); /* clear pause */
906 bit_set(gEmu
.playmsg
, 59, true); /* set stop */
910 /* convert value to MM:SS */
911 time
= get_playtime();
912 gEmu
.time
= time
; /* copy it */
913 gEmu
.playmsg
[7] = time
/ (10*60);
914 time
-= gEmu
.playmsg
[7] * (10*60);
915 gEmu
.playmsg
[8] = time
/ 60;
916 time
-= gEmu
.playmsg
[8] * 60;
917 gEmu
.playmsg
[9] = time
/ 10;
918 time
-= gEmu
.playmsg
[9] * 10;
919 gEmu
.playmsg
[10] = time
;
923 gEmu
.playmsg
[2] = gEmu
.set_state
; /* in FF/FR, report that instead */
927 gEmu
.playmsg
[3] = track
/ 10;
928 gEmu
.playmsg
[4] = track
% 10;
931 /* update the disk status message with Rockbox info */
932 void get_diskmsg(void)
934 int tracks
= rb
->playlist_amount();
937 gEmu
.diskmsg
[5] = tracks
/ 10;
938 gEmu
.diskmsg
[6] = tracks
% 10;
941 /* return the current track time in seconds */
942 int get_playtime(void)
944 struct mp3entry
* p_mp3entry
;
946 p_mp3entry
= rb
->audio_current_track();
947 if (p_mp3entry
== NULL
)
950 return p_mp3entry
->elapsed
/ 1000;
953 /* return the total length of the current track */
954 int get_tracklength(void)
956 struct mp3entry
* p_mp3entry
;
958 p_mp3entry
= rb
->audio_current_track();
959 if (p_mp3entry
== NULL
)
962 return p_mp3entry
->length
/ 1000;
965 /* change to a new track */
966 void set_track(int selected
)
968 if (selected
> get_track())
970 print_scroll("audio_next");
973 else if (selected
< get_track())
975 print_scroll("audio_prev");
980 /* return the track number */
983 struct mp3entry
* p_mp3entry
;
985 p_mp3entry
= rb
->audio_current_track();
986 if (p_mp3entry
== NULL
)
989 return p_mp3entry
->index
+ 1; /* track numbers start with 1 */
992 /* start or resume playback */
995 if (rb
->audio_status() == AUDIO_STATUS_PLAY
)
998 if (rb
->audio_status() == (AUDIO_STATUS_PLAY
| AUDIO_STATUS_PAUSE
))
1000 print_scroll("audio_resume");
1005 print_scroll("audio_play(0)");
1010 /* pause playback */
1011 void set_pause(void)
1013 if (rb
->audio_status() == AUDIO_STATUS_PLAY
)
1015 print_scroll("audio_pause");
1023 if (rb
->audio_status() & AUDIO_STATUS_PLAY
)
1025 print_scroll("audio_stop");
1031 void set_position(int seconds
)
1033 if (rb
->audio_status() & AUDIO_STATUS_PLAY
)
1035 print_scroll("audio_ff_rewind");
1036 rb
->audio_ff_rewind(seconds
* 1000);
1040 /****************** main thread + helper ******************/
1042 /* set to everything flat and 0 dB volume */
1043 void sound_neutral(void)
1044 { /* neutral sound settings */
1045 rb
->sound_set(SOUND_BASS
, 0);
1046 rb
->sound_set(SOUND_TREBLE
, 0);
1047 rb
->sound_set(SOUND_BALANCE
, 0);
1048 rb
->sound_set(SOUND_VOLUME
, 0);
1049 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1050 rb
->sound_set(SOUND_LOUDNESS
, 0);
1051 rb
->sound_set(SOUND_SUPERBASS
, 0);
1052 rb
->sound_set(SOUND_AVC
, 0);
1056 /* return to user settings */
1057 void sound_normal(void)
1058 { /* restore sound settings */
1059 rb
->sound_set(SOUND_BASS
, rb
->global_settings
->bass
);
1060 rb
->sound_set(SOUND_TREBLE
, rb
->global_settings
->treble
);
1061 rb
->sound_set(SOUND_BALANCE
, rb
->global_settings
->balance
);
1062 rb
->sound_set(SOUND_VOLUME
, rb
->global_settings
->volume
);
1063 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1064 rb
->sound_set(SOUND_LOUDNESS
, rb
->global_settings
->loudness
);
1065 rb
->sound_set(SOUND_SUPERBASS
, rb
->global_settings
->superbass
);
1066 rb
->sound_set(SOUND_AVC
, rb
->global_settings
->avc
);
1070 /* the thread running it all */
1074 unsigned char mbus_msg
[MBUS_MAX_SIZE
];
1076 bool connected
= false;
1077 long last_tick
= *rb
->current_tick
; /* for 1 sec tick */
1081 msg_size
= mbus_receive(mbus_msg
, sizeof(mbus_msg
), 1);
1083 { /* received something */
1084 if(gTread
.foreground
)
1086 dump_packet(buf
, sizeof(buf
), mbus_msg
, msg_size
);
1087 /*print_scroll(buf); */
1089 if (msg_size
> 2 && mbus_msg
[0] == 1
1090 && mbus_msg
[msg_size
-1] == calc_checksum(mbus_msg
, msg_size
-1))
1091 { /* sanity and checksum OK */
1093 { /* with the first received packet: */
1094 sound_neutral(); /* set to flat and 0dB volume */
1097 emu_process_packet(mbus_msg
, msg_size
-1); /* pass without chksum */
1099 else if(gTread
.foreground
)
1101 print_scroll("bad packet");
1104 else if (msg_size
< 0 && gTread
.foreground
)
1106 rb
->snprintf(buf
, sizeof(buf
), "rcv error %d", msg_size
);
1110 if (*rb
->current_tick
- last_tick
>= gEmu
.poll_interval
)
1111 { /* call the emulation regulary */
1113 last_tick
+= gEmu
.poll_interval
;
1116 } while (!gTread
.exiting
);
1119 /* callback to end the TSR plugin, called before a new one gets loaded */
1120 bool exit_tsr(bool reenter
)
1123 return false; /* dont let it start again */
1124 gTread
.exiting
= true; /* tell the thread to end */
1125 rb
->thread_wait(gTread
.thread
); /* wait until it did */
1127 uart_init(BAUDRATE
); /* return to standard baudrate */
1128 IPRE
= (IPRE
& ~0xF000); /* UART interrupt off */
1129 timer_set_mode(TM_OFF
); /* timer interrupt off */
1131 sound_normal(); /* restore sound settings */
1135 /****************** main ******************/
1138 int main(const void* parameter
)
1147 mbus_init(); /* init the M-Bus layer */
1148 emu_init(); /* init emulator */
1150 rb
->splash(HZ
/5, "Alpine CDC"); /* be quick on autostart */
1153 print_scroll("Alpine M-Bus Test");
1154 print_scroll("any key to TSR");
1157 /* init the worker thread */
1158 stack
= rb
->plugin_get_buffer((size_t *)&stacksize
); /* use the rest as stack */
1159 stack
= (void*)(((unsigned int)stack
+ 100) & ~3); /* a bit away, 32 bit align */
1160 stacksize
= (stacksize
- 100) & ~3;
1161 if (stacksize
< DEFAULT_STACK_SIZE
)
1163 rb
->splash(HZ
*2, "Out of memory");
1167 rb
->memset(&gTread
, 0, sizeof(gTread
));
1168 gTread
.foreground
= true;
1169 gTread
.thread
= rb
->create_thread(thread
, stack
, stacksize
, 0, "CDC"
1170 IF_PRIO(, PRIORITY_BACKGROUND
)
1176 button
= rb
->button_get(true);
1177 } while (button
& BUTTON_REL
);
1180 gTread
.foreground
= false; /* we're in the background now */
1181 rb
->plugin_tsr(exit_tsr
); /* stay resident */
1184 return rb
->default_event_handler(button
);
1191 /***************** Plugin Entry Point *****************/
1194 enum plugin_status
plugin_start(const void* parameter
)
1196 /* now go ahead and have fun! */
1197 return (main(parameter
)==0) ? PLUGIN_OK
: PLUGIN_ERROR
;
1200 #endif /* CONFIG_CPU==SH7034 && !(CONFIG_STORAGE & STORAGE_MMC) */