El propósito de este proyecto era crear un reproductor de sonido que pudiera reproducir sonido de alta calidad usando nada más que un solo chip (más una tarjeta SD para el almacenamiento de datos).
El microcontrolador elegido fue el PIC12F1840. Fue elegido por su alta frecuencia de reloj (hasta 32 MHz). Dado que los PIC de microchip tienen esta atroz propiedad de ejecutar una instrucción cada cuatro ciclos de reloj, esto da como resultado un rendimiento de 8MIPS. La segunda razón para la elección fue el módulo SPI de hardware: el SPI de golpe de bits no es difícil, PERO hacerlo lo suficientemente rápido para mantener la reproducción de sonido sería: el módulo SPI de hardware ayuda al permitir que el reloj SPI sea tan alto a 8MHz sin usando casi tanto tiempo de CPU como lo haría el bit-banging. El restablecimiento del diseño del circuito fue fácil: el PIC emite una forma de onda PWM para impulsar el altavoz, que es amplificado por un MOSFET, el controlador SPI de PIC habla con la tarjeta SD, y otro GPIO se usa para proporcionar energía a la tarjeta SD, lo que permite que se apague y, por lo tanto, deje que el circuito duerma en modo de muy baja energía (nanovatios de energía usados). La imagen es overclockeada usando el registro OSCTUNE a 33MHz desde la velocidad estándar de 32MHz.
El controlador de la tarjeta SD es mi propio trabajo y es compatible con SD / MMC / SDHC / RS-MMC / HS-MMC / miniSD / microSD / transFlash y cualquier otro componente de esa familia. La tarjeta se inicializa a 375 KHz (que está dentro de las especificaciones), y luego el bus se sincroniza a 8 MHz para lectura / escritura de datos. El controlador es peculiar por algunas razones, principalmente porque no intenta ser una abstracción entre el bus SPI y la persona que llama, por lo tanto, el controlador iniciará la tarjeta y la configurará para transmitir sectores a la persona que llama. La persona que llama luego llama a spiByte () directamente para leer los datos del sector. Cada 512 llamadas, se necesita una llamada a sdNextSector () para notificar al controlador SD que queremos el siguiente sector y esperar correctamente a que se inicie.
Dado que hay un tiempo entre el final de la transmisión de un sector y el comienzo de la transmisión del otro, el almacenamiento en búfer se utiliza para los datos de audio. El búfer es un búfer circular simple desde el que lee el hilo de audio y en el que escribe el hilo SD. Cuando el búfer está lleno, el subproceso SD ocupado espera a que se abra una ranura para escribir datos. Dado que a las tarjetas SD no les importa cuánto tiempo pasa entre los tictacs del reloj, esta espera puede ser tan larga como sea necesario.
El controlador del sistema de archivos (uFAT) también es mi propio trabajo, y es una implementación diminuta de FAT16. Solo admite: nombres de archivo cortos, solo FAT16, solo lectura, solo directorio raíz. Puede: enumerar los nombres / tamaños / banderas de los archivos y devolver el conjunto de extensiones de sector que ocupa un archivo. Una extensión de sector se define como una gama contigua de sectores (un "fragmento" en términos FAT). La persona que llama luego lee esos sectores por sí mismo (lo que permite que la persona que llama evite la capa innecesaria de abstracción y, por lo tanto, la pérdida de tiempo). En la última versión, EEPROM se utiliza después de todo, para ahorrar espacio.
La reproducción de audio en sí es relativamente simple. El módulo PWM se utiliza para generar el gusano de onda. Su frecuencia es de 1MHz aproximadamente (ajustable) y el ciclo de trabajo es la amplitud de audio necesaria. Actualmente se utilizan 6 bits de datos (los 2 inferiores están truncados). Esto produce un sonido de muy buena calidad con graves profundos y agudos agradables y nítidos. La interrupción de desbordamiento del temporizador 0 se utiliza para determinar cuándo cambiar el ciclo de trabajo de PWM al valor de la siguiente muestra de audio. Los ciclos de trabajo se mantienen siempre por debajo del 50%, ya que cualquier cosa por encima de eso provoca distorsión. El hilo de audio NO comprueba el subdesbordamiento del búfer y, en su lugar, sigue reproduciendo el búfer una y otra vez. No hace falta decir que evitamos esto llenando el búfer de manera oportuna. La función audioOn () activa el hilo de audio y audioOff () lo desactiva.
El generador de números aleatorios utilizado es un tipo muy simple de módulo de multiplicación-suma de 32 bits, donde Xn = (Xn-1 * 0xDEECE66D + 0x0B)% 0x100000000. La semilla se almacena en los últimos 4 bytes de EEPROM, de modo que en cada encendido la secuencia de sondas no se repite, sino que continúa desde la última vez. Los 16 bits superiores de la semilla modificada se devuelven como el número "aleatorio" a la persona que llama.
Dado todo lo anterior, el método de operación se vuelve simple:
Encender la tarjeta SD
Inicializar tarjeta SD
Lista de archivos en el directorio raíz
Contar el número de archivos no ocultos con la extensión "WAV"
Círculo:
Seleccionar un archivo al azar
Use uFAT para encontrar el archivo de extensiones de sector que ocupa (la fragmentación se maneja correctamente)
Almacenar esos sectores en el sectorList storage
Iniciar reproducción de audio
Reproducir datos de sectores en cada extensión hasta que no haya más
apagar el audio
entrar en suspensión de baja potencia durante un tiempo determinado (30 segundos actualmente, duración aleatoria en el futuro)
tarjeta de energía encendida
[Reiniciar] tarjeta de inicio
Uso detallado de recursos PIC:
Relojes: 33MHz al reproducir, 500KHz al iniciar luego de la función SLEEP antes de decidir suspender de nuevo
WDT: se usa para encender luego de SLEEP
Módulo de reloj de referencia: sin usar
Interrupciones: interrupción de desbordamiento del temporizador 0 utilizada, otras no utilizadas
EEPROM de datos: 4 bytes al final utilizados, otros no utilizados
Programa Flash: el código usa el 40%, sectorList usa el 45%
GPIO: 3 usados para SPI, 1 usado para salida, 1 usado para control de energía de la tarjeta SD. RA3 sin usar.
Referencia de voltaje fijo: sin usar
Indicador de temperatura: sin usar
ADC: sin usar
DAC: sin usar
SR Latch: sin usar
Comparador: sin usar
Timer0: se utiliza para la sincronización de la reproducción de sonido, se desborda 22050 veces por segundo, con el valor de recarga adecuado
Timer1: sin usar
Timer2: utilizado para la sincronización del módulo PWM
Modulador de señal de datos: sin usar
PWM: utilizado para producir sonido de salida
MSSP: se utiliza en el modo maestro SPI para hablar con la tarjeta SD
EUSART: Sin usar
Módulo de detección capacitativo: sin usar
Para abordar algunas preguntas finales: Los archivos de audio son archivos WAV sin comprimir. El cde intenta averiguar el formato específico y jugar bien con él, pero las muestras de 8 bits funcionan mejor. La tarjeta SD debe estar formateada en FAT16 ya que uFAT no es compatible con FAT12 o FAT32. Esto puede (y probablemente lo hará) cambiar en el futuro.
El número máximo de fragmentos de archivo se establece en el código, y actualmente es algo así como 128, no se reproducirán más de los que no se reproducirán o se dañarán los datos, así que desfragmenta tu tarjeta a veces, si planeas reemplazar archivos de sonido con frecuencia. La limpieza del código está en camino y el código se publicará una vez que se complete la limpieza. Por ahora, aquí está el archivo HEX para el PIC12F1840
El código, tal como está ahora, leerá cualquier archivo WAV, mono o estéreo, cualquier frecuencia de muestreo y tamaño de muestra. Sin embargo, la muestra mono de 8 bits funciona mejor. Audacity es un programa gratuito y de código abierto que se puede utilizar para convertir archivos de audio entre formatos. La entrada FAT en Wikipedia es un buen punto de partida para obtener información FAT16. De hecho, es suficiente implementar un controlador FAT16 rudimentario.
Código HEX
Código: Seleccionar todo
:020000040000FA
:1000000080312628FF3FFF3F80310B1D090045301E
:10001000200095006C08963E8600203003182130A1
:1000200087000108F100EC0A710E30390C38F0003D
:10003000F136F13671082500910070089300200018
:100040006C085A3A0319EC010B110900880183313D
:10005000DB2BD100210065082000D50021006408B9
:100060002000D4002100640A0319650A0319003432
:100070003E28540A0319550A03195A282000550826
:10008000D3005408D2005308C5005208C400FC2015
:1000900080314508D5004408D4005208013ECF0005
:1000A0000030533DD0005506031D582854084F0614
:1000B000031939282100640820005202CF002100D2
:1000C00065082000533BD0004F08013ED6000030A9
:1000D000503DD700D801D9016D08F200F301F401B9
:1000E000F5015908F9005808F8005708F7005608B4
:1000F000F600D72080317508D9007408D80073083D
:10010000D7007208D6004E08860087015608C03F07
:100110005708C13F5808C23F5908C33F210064082F
:10012000FE3E2000D600FF302100653D2000D700B4
:10013000D801D9016D08F200F301F401F501590865
:10014000F9005808F8005708F7005608F600D720BD
:1001500080317508D9007408D8007308D700720878
:10016000D60021004D082000D60721004E082000AF
:10017000D73D0030D83DD93D6608D6076708D73D42
:100180006808D83D6908D93D51088600870156089E
:10019000C03F5708C13F5808C23F5908C33F5508E0
:1001A0002100E500200054082100E4000134A001F2
:1001B000A101A201A301721CE5287608A007770817
:1001C000A13D7808A23D7908A33DF635F70DF80D5D
:1001D000F90DF536F40CF30CF20C75087404730485
:1001E0007204031DDB282308F5002208F40021080F
:1001F000F3002008F200080021004F082000C8008A
:10020000210050082000C900CA01CB016608C807B8
:100210006708C93D6808CA3D6908CB3D4508C8075D
:1002200001300318C9070318CA070318CB0745088C
:10023000CD004408CC00FF30CC05CD01CC35CD0D30
:100240004B08BF004A08BE004908BD004808BC0072
:100250004D08C1004C08C0000230C2004630C30047
:100260008E31012680310038031D3B29FF302000EC
:10027000C400C50008004630F2018F31E927803103
:1002800073082000C5007208C400FF304502F83032
:1002900003194402031C08003629B3001030F20091
:1002A0007E30F3000230F4007608E52181317208D7
:1002B000BE00B401B501B601B7013E0803196729B4
:1002C00033080319C22903303E020318C229103033
:1002D000F2003E30F3000C30F4007608E521813165
:1002E0007308B2007208B1001030F2002F30F30032
:1002F0000330F4007608E52181317208BC0010302B
:10030000F2005030F3000430F4007608E52181312A
:100310007208BD000730BF003F0803199729BD08C8
:1003200003199729BD03BF038C29BC03BF033F08F2
:10033000031DBC08031D95293108013EB80000309B
:10034000323DB900BA01BB013F08031D3818AE2980
:10035000BB36BA0CB90CB80CBF03A4293B08B700D4
:100360003A08B6003908B5003808B4003F083D0225
:100370003C07890ABF29B435B50DB60DB70D890BF9
:10038000BB29DC293E0BDC291030F2003030F300B1
:100390001630F4007608E52181317508B70074083D
:1003A000B6007308B5007208B4000A30B435B50D54
:1003B000B60DB70D890BD6293708F9003608F800B5
:1003C0003508F7003408F6000800A800AF017208ED
:1003D000090709070907AD00A901AA01AB01AC0192
:1003E00073082D02FF3EB000A000A036A036A03654
:1003F0002008AE0030080739073CB0002E08280757
:10040000A000860087010108A100300A082AA13651
:10041000890B072A21080139A200A3012F0A122AF9
:10042000A235A30D890B102A2208A4002308A500D9
:100430000030A51BFF30A600A7002408A90425084A
:10044000AA042608AB042708AC04AF0AF30AF40B8D
:10045000F0292C08F5002B08F4002A08F3002908DD
:10046000F200080020006908680467046604031DA0
:100470000034BE30DC000130DD00BC01BD01BE0136
:10048000BF015D08C1005C08C0001030C2004C30E4
:10049000C3008E3101268231003803190034200058
:1004A00050030319672A5008043A0319672A5008B1
:1004B000063A0319672A50080B3A0319672A5008AD
:1004C0000C3A0319672A50080E3A031DD22A57081E
:1004D000E600E701E801E9015608C400C501C601CC
:1004E000C7016608C8006708C9006808CA0069082B
:1004F000CB004A08CB004908CA004808C900C80117
:1005000044084804E60045084904E70046084A0450
:10051000E80047084B04E9005508C400C501C601BE
:10052000C7016608C8006708C9006808CA006908EA
:10053000CB004A08CB004908CA004808C900C801D6
:1005400044084804E60045084904E70046084A0410
:10055000E80047084B04E9005408C400C501C6017F
:10056000C7016608C8006708C9006808CA006908AA
:10057000CB004A08CB004908CA004808C900C80196
:1005800044084804E60045084904E70046084A04D0
:10059000E80047084B04E9008F3140278231003AD8
:1005A000031D013410302000DC070318DD0A013080
:1005B0005D02FE3003195C02031800343D2A730803
:1005C000AA007208A900A935AA0DA935AA0D6A08C2
:1005D000A9076B08AA3DB001AB01AC01AD01AE01AA
:1005E000AF0180302300950020002A0823009200EC
:1005F00020002908230091008B131514000000002F
:100600008B17230013082000A000230014082000EB
:10061000A100A201A3012B08A4002C08A5002D080D
:10062000A6002E08A7000E30A435A50DA60DA70D17
:10063000890B142B20082404AB0021082504AC00EE
:1006400022082604AD0023082704AE00AF0AA90A39
:100650000319AA0A02302F02031CF12AB008031D55
:10066000332B7408342B7508A800860087012B08EB
:10067000C03F2C08C13F2D08C23F2E08C33F0230A7
:10068000B00A300203180800EC2AF6007208F607D8
:1006900076088600870101082000A000A101A201C0
:1006A000A3017608013E86000108A400A501A60169
:1006B000A7012608A7002508A6002408A500A40174
:1006C0007608023E86000108A800A901AA01AB0134
:1006D0002908AB002808AA00A901A8017608033E52
:1006E00086000108AF00AE01AD01AC012C08A804E2
:1006F0002D08A9042E08AA042F08AB042808A40476
:100700002908A5042A08A6042B08A7042008240405
:10071000F20021082504F30022082604F40023082F
:100720002704F5000800AA004038A4002308A5000B
:100730002208A6002108A7002008A80005300314FD
:10074000F2002430F301B723F900790DA900AA01C2
:100750002A08243E8600870101088E31E82683316D
:1007600020000630AA0A2A0203180800A82BF50068
:10077000F801720878020318D82B78087507F4007E
:10078000860087010108F600F7018030F335730514
:10079000F4007608803974060319D02B0930F3066B
:1007A0000830F635F70A7702031CC52BF80AB92B77
:1007B00073087F39080096308400203085005A3055
:1007C000FE000030FF0084319D248331FB01E601EF
:1007D000E701E801E901EA01EB01EC01ED01CD30AF
:1007E0008400003085001330FE000030FF008431AB
:1007F0009D240130EE002100FF30E000FF30E100D9
:10080000FF30E200FF30E300FF30E400FF30E5009E
:1008100020008831DE28F5018030F2007508963E10
:10082000F300203003182130F400730886007408A8
:100830008700720881005A30F50A7502031C0C2CDF
:1008400004309C001F309B000C3025009300920167
:10085000910121008C12200095010B118B160800CC
:10086000B6003730AB01AC01AD01AE01AF01AF0A4C
:100870008C31F5248431B700B71F402C37080800AD
:1008800037193E2C3508AE003408AD003308AC00F3
:100890003208AB00AF01AF0A36088C31F52C2200CC
:1008A0008C100C1624001B309512F200F20B562C03
:1008B00022001B308C14F200F20B5C2C22001B3047
:1008C0008C10F200F20B622C240095160800F60141
:1008D000F701721C6F2C7408F6077508F73DF435A4
:1008E000F50DF336F20C73087204031D692C7708BA
:1008F000F3007608F2000800F301822C7408FF3A36
:10090000031D8C2CFF308E31E8268431F4008130B9
:10091000F30A7302031C7E2C74080800F200F2082C
:10092000031D952C0A30F300962CF301F4012400EA
:100930001508F03973049500080064008001013146
:100940000130FE020030FF3B7F087E0403190034B3
:100950009E2C0230BC01BD01BE01BF01C001C1001F
:10096000C201C3018E31012E8B122500930121009B
:100970008C1220008C120800FF3FFF3FFF3FFF3F1B
:10098000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F77
:10099000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F67
:1009A000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F57
:1009B000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F47
:1009C000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F37
:1009D000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F27
:1009E000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F17
:1009F000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F07
:100A00000034003400340034003400340034003446
:100A10000034003400340034003400340034003436
:100A20000034003400340034003400340034003426
:100A30000034003400340034003400340034003416
:100A40000034003400340034003400340034003406
:100A500000340034003400340034003400340034F6
:100A600000340034003400340034003400340034E6
:100A700000340034003400340034003400340034D6
:100A800000340034003400340034003400340034C6
:100A900000340034003400340034003400340034B6
:100AA00000340034003400340034003400340034A6
:100AB0000034003400340034003400340034003496
:100AC0000034003400340034003400340034003486
:100AD0000034003400340034003400340034003476
:100AE0000034003400340034003400340034003466
:100AF0000034003400340034003400340034003456
:100B00000034003400340034003400340034003445
:100B10000034003400340034003400340034003435
:100B20000034003400340034003400340034003425
:100B30000034003400340034003400340034003415
:100B40000034003400340034003400340034003405
:100B500000340034003400340034003400340034F5
:100B600000340034003400340034003400340034E5
:100B700000340034003400340034003400340034D5
:100B800000340034003400340034003400340034C5
:100B900000340034003400340034003400340034B5
:100BA00000340034003400340034003400340034A5
:100BB0000034003400340034003400340034003495
:100BC0000034003400340034003400340034003485
:100BD0000034003400340034003400340034003475
:100BE0000034003400340034003400340034003465
:100BF0000034003400340034003400340034003455
:100C00000034003400340034003400340034003444
:100C10000034003400340034003400340034003434
:100C20000034003400340034003400340034003424
:100C30000034003400340034003400340034003414
:100C40000034003400340034003400340034003404
:100C500000340034003400340034003400340034F4
:100C600000340034003400340034003400340034E4
:100C700000340034003400340034003400340034D4
:100C800000340034003400340034003400340034C4
:100C900000340034003400340034003400340034B4
:100CA00000340034003400340034003400340034A4
:100CB0000034003400340034003400340034003494
:100CC0000034003400340034003400340034003484
:100CD0000034003400340034003400340034003474
:100CE0000034003400340034003400340034003464
:100CF0000034003400340034003400340034003454
:100D00000034003400340034003400340034003443
:100D10000034003400340034003400340034003433
:100D20000034003400340034003400340034003423
:100D30000034003400340034003400340034003413
:100D40000034003400340034003400340034003403
:100D500000340034003400340034003400340034F3
:100D600000340034003400340034003400340034E3
:100D700000340034003400340034003400340034D3
:100D800000340034003400340034003400340034C3
:100D900000340034003400340034003400340034B3
:100DA00000340034003400340034003400340034A3
:100DB0000034003400340034003400340034003493
:100DC0000034003400340034003400340034003483
:100DD0000034003400340034003400340034003473
:100DE0000034003400340034003400340034003463
:100DF0000034003400340034003400340034003453
:100E00000034FF3FFF3FFF3FFF3FFF3FFF3FFF3FFC
:100E1000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FE2
:100E2000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FD2
:100E3000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FC2
:100E4000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FB2
:100E5000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FA2
:100E6000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F92
:100E7000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F82
:100E8000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F72
:100E9000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F62
:100EA000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F52
:100EB000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F42
:100EC000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F32
:100ED000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F22
:100EE000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F12
:100EF000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F02
:100F0000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FF1
:100F1000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FE1
:100F2000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FD1
:100F3000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FC1
:100F4000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FB1
:100F5000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3FA1
:100F6000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F91
:100F7000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F81
:100F8000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F71
:100F9000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F61
:100FA000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F51
:100FB000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F41
:100FC000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F31
:100FD000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F21
:100FE000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F11
:100FF000FF3FFF3FFF3FFF3FFF3FFF3FFF3FFF3F01
:10100000FF3FFF3FFF3FB600043E860087013508E3
:10101000BA003408B9003308B8003208B700011824
:1010200018280930B735B80DB90DBA0D890B12283B
:101030003A08AE003908AD003808AC003708AB00FC
:101040001230AF01F5248831BB00BB1F2A28EF30D6
:101050008D220034FF30E82688312000BB00FF3AA3
:1010600003192A283B08FE3A03190134EE302828D8
:1010700020000530CC01CB01CA00F430C900FA30A1
:10108000CD00CE01CF01D001C101C20184310B24BA
:1010900088314208F3004108F2004930F4004D3035
:1010A000F5008231DF228831C10A0319C20A4C08D7
:1010B0004B044A044904031D632850084F044E049E
:1010C0004D040319CE284C08B5004B08B4004A085B
:1010D000B3004908B200DB30032088312000C00093
:1010E000C008031D76280230212288312000C40167
:1010F000C501C601C70150084702031D89284F08D2
:101100004602031D89284E084502031D89284D0803
:1011100044020318CB284708460445044404031935
:101120009528DB30F300D02088312000C801C301AE
:101130004308803A0319BE286E086C0603199828E4
:10114000FF30E82688312000BC00BC36BC36BC36F7
:101150003C08BD006E08963EBE00203003182130CA
:10116000BF003E0886003F0887003D088100EE0A68
:101170006E085A3A0319EE01C30A98280430C80AC7
:101180004802031C9728C40A0319C50A0319C60A92
:101190000319C70A7B28F026883149288431B42CEA
:1011A000FF30E8268831FF30E8268831FF30E82616
:1011B0008831F400FF3A031D0800D6282100F030E2
:1011C000CB01CC01C101C20199001F3098008030D1
:1011D0008B00413022009D00CC3021008C00230088
:1011E0008C0180302100950003301710AF00AC3027
:1011F000AE00B330AD00AD0BFB28AE0BFB28AF0B40
:10120000FB28640015302100AF00DA30AE0020303A
:10121000AD00AD0B0929AE0B0929AF0B0929102927
:101220002000DB300C14292488312100C500C508BA
:10123000031D1D29013021228831FE2388314027DA
:1012400088312100C500C508031D29290630212247
:10125000883121004C082000C50021004B082000E7
:10126000C400B030C600C730C700C630C800C330A5
:10127000C9000A2588310038031956292100CB0AF4
:101280000319CC0A46081239031D29293808573A90
:10129000031D29293908413A031D29293A08563ADC
:1012A000031D2929C10A0319C20A2929210042085C
:1012B0004104031D5E2903302122883121004208A8
:1012C0002000A500210041082000A40097228831B9
:1012D0002908A7002808A60009248831250821002C
:1012E000BC00200024082100BB00CB01CC014C082D
:1012F0002000C50021004B082000C400B030C6000B
:10130000C730C700C630C800C330C9000A258831BD
:1013100000380319A9292100CB0A0319CC0A460871
:101320001239031D77293808573A031D77293908E0
:10133000413A031D77293A08563A031D77290130AF
:10134000BB020030BC3B3B0A03193C0A0319AC2921
:10135000772909302122883121004408F30043080D
:10136000F200042488310038031DB92907302122F6
:101370008831872388315B23CB01CC01C730200023
:10138000CE00BD3080312920883100380319F6297C
:101390004C082000A10021004B082000A0002100E3
:1013A00040082000A50021003F082000A4002100E3
:1013B0003E082000A30021003D082000A2002100DB
:1013C0004A082000A900210049082000A8002100A7
:1013D00048082000A700210047082000A6002A2274
:1013E00088312100CB0A0319CC0ABE294C08200001
:1013F000A10021004B082000A000A201A301A4012C
:10140000A501A601A701A801A9012A228431A924C6
:1014100088313820883175300C10F501F401F30063
:101420003030F200B12388312000DB300C14292445
:1014300088312100C500C508031D5E2901305C29E3
:10144000202AF500FA308D22883175088D228831E6
:101450006300282A2108AF002008AE00AE35AF0D8A
:10146000AE35AF0D6A08AE076B08AF3DB101B108EC
:10147000031D422A2508AD002408AC002308AB0058
:101480002208492A2908AD002808AC002708AB002B
:101490002608AA00B00184302300950020002F0800
:1014A0002300920020002E082300910020002A082B
:1014B000F2002B08F3002C08F4002D08F5000E3084
:1014C000F536F40CF30CF20C890B602A7308230038
:1014D00094007208930055308B139600AA30960042
:1014E0009514000000008B1723009518742A200023
:1014F000B00AAE0A0319AF0A0E30AA35AB0DAC0D17
:10150000AD0D890B7D2A02303002031C4B2A0230BC
:10151000B10A310203180800372AF400F2007B08F0
:1015200049238831FB0A7B08031D0800932AFF30FA
:10153000412388312000B200FE30B301B401B5016F
:10154000412388312000AA00AB01AC01AD01320873
:10155000AE003308AF003408B0003508B1003008E1
:10156000B1002F08B0002E08AF00AE012A082E04EB
:10157000B2002B082F04B3002C083004B4002D084F
:101580003104B500FD30412388312000AA00AB01B1
:10159000AC01AD013208AE003308AF003408B00032
:1015A0003508B1003008B1002F08B0002E08AF0098
:1015B000AE012A082E04B2002B082F04B3002C0819
:1015C0003004B4002D083104B500FC3041238831CB
:1015D0002000AA00AB01AC01AD013208AE00330817
:1015E000AF003408B0003508B1003008B1002F0852
:1015F000B0002E08AF00AE012A082E04B2002B085E
:101600002F04B3002C083004B4002D083104B500B9
:10161000F5003408F4003308F3003208F200DE303D
:10162000F900EC30F800E630F7006D30F60080315C
:10163000D72088310B307207B2000030733DB30001
:101640000030743DB4000030753DB500F200FF304D
:101650004923883120003408F200FE3049238831C4
:1016600020003308F200FD30492388312000320881
:10167000F200FC30492320003508A9003408A800F6
:101680000800F20023009501720891001514130858
:101690000800F300043023009500730891007208DD
:1016A00093008B1355309600AA30960095148B1733
:1016B000951C0800582B20006B08F3006A08F20004
:1016C000F401F501210056087502031D692B550828
:1016D00074020318080094308B13230095007308DC
:1016E00092007208910055309600AA309600951429
:1016F000000000008B172300151A7B2B0830F4071D
:101700000318F50A2030F2070318F30A622B2100B0
:101710000230D501D60001302000EA008530EB0010
:101720006A08F2001F30F205720803199A2B72083A
:10173000203CF20072082100D502031CD603720877
:101740002000EA070318EB0AE0302100D505FF303E
:10175000D6052000EB132100D636D50CD636D50C95
:1017600008000430A501A400A301A201210019086A
:101770002000A00013302100990020008C122100CD
:101780000C082000A100DE3021008C002000123067
:101790000C10A600200025087502031DD92B240873
:1017A0007402031DD92B23087302031DD92B2208B1
:1017B0007202031CEC2B640003142000260D210090
:1017C000970020002208F2022308F33B2408F43B90
:1017D0002508F53B6300CA2BA603A536A40CA30C71
:1017E000A20CA61FCA2B21001710200021082100DF
:1017F0008C00200020082100990008002000E6014C
:10180000E701E801E90108007308E5007208E40057
:101810000134250824040319242CFA01FA0AA51B13
:10182000142CA435A50D0E2C25082702031D1A2CF7
:1018300024082602031C202C2408A6022508A73B06
:10184000A536A40CFA0B142C2708A5002608A40022
:101850000800D500043E8600870181105508043E2B
:1018600086000111F827003084318E248831200051
:10187000D601FF30E826883120001430D60A5602FF
:10188000031C392C84314F24883120000030AB01F7
:10189000AC01AD01AE01AF01AF0AF5248831D6002D
:1018A000560B422C0130AE01AD01AC00AA30AB00AA
:1018B0000830AF01AF0AF5248831D600D61B0034BA
:1018C0000030561D0130C4005508043E86008701D3
:1018D00001084406FE39440681003730AB01AC01F3
:1018E000AD01AE01AF01AF0AF5248831D600D61B99
:1018F00000340030561D0130C4005508043E8600F7
:101900008701C40DC40D01084406FB39440681005B
:10191000B801B80A5508043E8600010C890C01394B
:10192000F92688310038031DA22CB8015508043E61
:1019300086008701010C890C0139F92688310038AD
:10194000031900340930AB01AC01AD01AE01AF01A8
:10195000F5248831D600D61B00341030F500453010
:10196000F601F701F801F80AA42688312000D60014
:101970005608031D00344530F6005508043E860025
:1019800000308701011D013081314D21883155081A
:10199000860087017608C03F7708C13F7808C23FBC
:1019A0007908C33F5508043E860002308114AE0119
:1019B000AD01AC001030AB01AF01AF0AF5248831A6
:1019C000D6005608031D00343B30AB01AC01AD011D
:1019D000AE01AF01AF0AF5248831D6005608031DC9
:1019E0000034013084318E240134B000FF30E82609
:1019F00020002E08A3002D08A2002C08A1002B080F
:101A0000A000300883319323883184317C24200066
:101A1000B1000800210053082000D2002100540822
:101A20002000D300D401D5016608D2076708D33D52
:101A30006808D43D6908D53DD601D701CA01CB015C
:101A40002100520820004B02031D2A2D21005108BD
:101A500020004A02031800345508BF005408BE0095
:101A60005308BD005208BC005708C1005608C0000A
:101A70004E30C201C20AC300012688310038031962
:101A800000342000CE08031900344E08E53A03194B
:101A9000EB2D4E082E3A0319EB2D0130C402003015
:101AA000C53B440A0319450A031DEB2D46080319DB
:101AB0007E2D4E08053A03195F2D4E08602DE53046
:101AC000CC00CD014608860087014C0881005508EE
:101AD000BF005408BE005308BD005208BC005608A1
:101AE000013EC0000030573DC1000A30C200460A26
:101AF000C30001268831003803190034200048084B
:101B00000319992D5508BF005408BE005308BD00A5
:101B10005208BC0056080B3EC0000030573DC100C3
:101B2000C201C20A4808C3000126883100380319DF
:101B30000034200049080319C02D5508BF0054087F
:101B4000BE005308BD005208BC0056081A3EC00033
:101B50000030573DC1000230C2004E30C3000126A4
:101B600088310038031900344E30F201E9278831FA
:101B700020004908860087017208C03F7308C13FF2
:101B80004708031901345508BF005408BE00530824
:101B9000BD005208BC0056081C3EC0000030573D36
:101BA000C1000430C2004E30C30001268831003825
:101BB000031900344E30F201833145238831470840
:101BC000860087017208C03F7308C13F7408C23F96
:101BD0007508C33F01342030D6070318D70A5708C9
:101BE000023A5604031DFD2DD601D701D20A03196E
:101BF000D30A0319D40A0319D50ACA0A0319CB0A4E
:101C0000202D2100630820003F06031D172E210010
:101C1000620820003E06031D172E210061082000E7
:101C20003D06031D172E2100600820003C06031D01
:101C3000252E21005A0820004102031D232E2100D9
:101C400059082000400203186D2E2100600A031974
:101C5000610A0319620A0319630A031D332E452E14
:101C6000FF30E82688312100D90A0319DA0A59031E
:101C7000023003195A06031D302EF0268831FF303A
:101C80002100E300E200E100E00020003F08210025
:101C9000E30020003E082100E20020003D08210072
:101CA000E10020003C082100E00063082000B500AE
:101CB000210062082000B400210061082000B30068
:101CC000210060082000B200DB300320883100389A
:101CD000031900342100D901DA0120004108210054
:101CE0005A06031D772E20004008210059060319CB
:101CF000802ED90A0319DA0AFF30E82688316D2EC2
:101D0000200042082100D9070318DA0A2000C20384
:101D10004208FF3A0319962E43088600FF308701D8
:101D2000E826883181002000C30A862E21005A0847
:101D3000023A5904031D0134F026FF302100E3006C
:101D4000E200E100E0000134A00084317C2488310D
:101D50002000A100FE3A0319B82E2108080020082F
:101D60008600FF308701E826883181002000A00A24
:101D70000130F5020030F63B750A0319760A031D9F
:101D8000AF2E7708FF300319E02EE8268831F900DE
:101D9000FA01FA00F9017708860087017908C03F47
:101DA0007A08C13FFF30E8268831F900FA01770848
:101DB000860087017908810441317A088104E52E83
:101DC000E8268831FF30E8268831FF30E8260034E5
:101DD000F200720824009100141CEC2E1108080077
:101DE00020000C30AB01AC01AD01AE01AF01AF0A78
:101DF000F52CB900BA01BB01BE01BE0AB808031D2B
:101E0000062FC001C101C201C3010B2F4030C30026
:101E1000C201C101C001BA0A0319BB0A13303B0257
:101E2000893003193A0203180034B908031D262F1C
:101E30004308AE004208AD004108AC004008AB00CA
:101E40000130AF01AF0AF5248831322F4308B500C5
:101E50004208B4004108B3004008B2002930843180
:101E600030248831BC00BD01BF00BF1B00343E08D8
:101E700003193D2FC216BE010B2F3F1C01340B2F3F
:101E80006908BF006808BE006708BD006608BC009E
:101E90003630C0000430C101C200A030C3000126AA
:101EA000883100380319003421002008463A031D08
:101EB000652F2108413A031D652F2208543A031D5E
:101EC000652F2308313A0319672F8231322A200007
:101ED0006908BF006808BE006708BD006608BC004E
:101EE0000B30C0000D30C101C200A030C30001267C
:101EF0008831003803190034A030F201E927883115
:101F00007308023A7204031D0034210022082000E5
:101F1000ED000330F200A030E9278831730821007A
:101F2000D0007208CF000630F200A030E9278831D7
:101F30007308D2007208D1000B30F200A030E927FC
:101F40007308D8007208D7002508F200F30158087A
:101F5000F5005708F4008431672488317308D400F1
:101F60007208D3004F08D3075008D43DFF3020003B
:101F7000DE000130DF00E001E10121005108200016
:101F8000E200210052082000E3000530E401E501F1
:101F9000E235E30DE40DE50D890BC82F6208DE077D
:101FA0006308DF3D6408E03D6508E13D0930E13646
:101FB000E00CDF0CDE0C890BD72F5F082100CE0070
:101FC00020005E082100CD005308CD075408CE3D07
:101FD0000134F4007208F40774088600870174085D
:101FE000013E840085010008F3000108F2000800AA
:101FF0001530240092002A309500C030940008006B
:020000040001F9
:10E00000FF00FF00FF00FF00FF00FF00FF00FF0018
:10E01000FF00FF00FF00FF00FF00FF00FF00FF0008
:10E02000FF00FF00FF00FF00FF00FF00FF00FF00F8
:10E03000FF00FF00FF00FF00FF00FF00FF00FF00E8
:10E04000FF00FF00FF00FF00FF00FF00FF00FF00D8
:10E05000FF00FF00FF00FF00FF00FF00FF00FF00C8
:10E06000FF00FF00FF00FF00FF00FF00FF00FF00B8
:10E07000FF00FF00FF00FF00FF00FF00FF00FF00A8
:10E08000FF00FF00FF00FF00FF00FF00FF00FF0098
:10E09000FF00FF00FF00FF00FF00FF00FF00FF0088
:10E0A000FF00FF00FF00FF00FF00FF00FF00FF0078
:10E0B000FF00FF00FF00FF00FF00FF00FF00FF0068
:10E0C000FF00FF00FF00FF00FF00FF00FF00FF0058
:10E0D000FF00FF00FF00FF00FF00FF00FF00FF0048
:10E0E000FF00FF00FF00FF00FF00FF00FF00FF0038
:10E0F000FF00FF00FF00FF00FF00FF00FF00FF0028
:10E10000FF00FF00FF00FF00FF00FF00FF00FF0017
:10E11000FF00FF00FF00FF00FF00FF00FF00FF0007
:10E12000FF00FF00FF00FF00FF00FF00FF00FF00F7
:10E13000FF00FF00FF00FF00FF00FF00FF00FF00E7
:10E14000FF00FF00FF00FF00FF00FF00FF00FF00D7
:10E15000FF00FF00FF00FF00FF00FF00FF00FF00C7
:10E16000FF00FF00FF00FF00FF00FF00FF00FF00B7
:10E17000FF00FF00FF00FF00FF00FF00FF00FF00A7
:10E18000FF00FF00FF00FF00FF00FF00FF00FF0097
:10E19000FF00FF00FF00FF00FF00FF00FF00FF0087
:10E1A000FF00FF00FF00FF00FF00FF00FF00FF0077
:10E1B000FF00FF00FF00FF00FF00FF00FF00FF0067
:10E1C000FF00FF00FF00FF00FF00FF00FF00FF0057
:10E1D000FF00FF00FF00FF00FF00FF00FF00FF0047
:10E1E000FF00FF00FF00FF00FF00FF00FF00FF0037
:10E1F000FF00FF00FF00FF00FF00FF00FF00FF0027
:04000E00ACC9FFDE9C
:08000000FF3FFF3FFF3FFF3F00
:00000001FF
ufat.c
Código: Seleccionar todo
#include "ufat.h"
#define UFAT_FLAG_VOLUME_LABEL 8
#define UFAT_FLAG_DEVICE 64
#define UFAT_FLAG_RESERVED 128
#define UFAT_FLAG_LFN (UFAT_FLAG_VOLUME_LABEL | UFAT_FLAG_READONLY | FAT_FLAG_HIDDEN | UFAT_FLAG_SYSTEM)
#define EOC_16 0xfff8
#define EOC_12 0xff8
#define CLUS_INVALID 0xffff
//fat16 only, very very limited
static u32 diskOffset = 0; //to beginning of fs
static u8 secPerClus;
static u16 rootDirEntries;
static u16 sectorsPerFat;
static u16 fatSec; //where fat begin
static u16 rootSec; //where root directory begins
static u16 dataSec; //where data begins
static u16 curClus = CLUS_INVALID;
static bool ufatParsePartitionTable(void){
char record[16];
u16 offset;
if(diskOffset) return false; //partitions inside partitions do not exist, probbay no fat FS on this disk - bail out
for(offset = 0x1BE; offset < 0x1FE; offset += 16){
if(!ufatExtRead(0, offset, 16, record)) return false;
if(record[4] != 1 && record[4] != 4 && record[4] != 6 && record[4] != 0x0B && record[4] != 0x0C && record[4] != 0x0E) continue; //not FAT parition
//we now have a contender - try to mount it
diskOffset = record[11];
diskOffset = (diskOffset << 8) | record[10];
diskOffset = (diskOffset << 8) | record[9];
diskOffset = (diskOffset << 8) | record[8];
if(ufatMount()) return true;
}
//if we got here, we failed - give up and cry
return false;
}
static u16 ufatGetU16(const char* v, u8 idx){
v += idx;
return (((u16)v[1]) << 8) | ((u16)v[0]);
}
static u32 ufatGetU32(const char* v, u8 idx){
v += idx;
return (((u32)v[3]) << 24) | (((u32)v[2]) << 16) | (((u32)v[1]) << 8) | ((u32)v[0]);
}
void ufatInit(void){
diskOffset = 0;
}
bool ufatMount(void){
char buf[13];
if(!ufatExtRead(diskOffset, 0x36, 4, buf)) return false;
if(buf[0] !='F' || buf[1] !='A' || buf[2] != 'T' || buf[3] != '1'){ //may be a partition table
return ufatParsePartitionTable();
}
if(!ufatExtRead(diskOffset, 0x0B, 13, buf)) return false;
if(ufatGetU16(buf, 0x0B - 0x0B) != 512) return false; //only 512 bytes/sector FSs supported
secPerClus = buf[0x0D - 0x0B];
fatSec = ufatGetU16(buf, 0x0E - 0x0B); //"reserved sectors" = sectors before first fat
rootDirEntries = ufatGetU16(buf, 0x11 - 0x0B);
sectorsPerFat = ufatGetU16(buf, 0x16 - 0x0B);
rootSec = fatSec + sectorsPerFat * (u16)(buf[0x10 - 0x0B]);
dataSec = rootSec + (((u32)rootDirEntries) * 32 + UFAT_DISK_SECTOR_SZ - 1) / UFAT_DISK_SECTOR_SZ;
return true;
}
bool ufatGetNthFile(u16 n, char* name, u32* sz, u8* flags, u16* id){
u16 i;
u32 sec = diskOffset + rootSec;
u16 offset = 0;
u8 buf[4];
for(i = 0; i < rootDirEntries; i++){
if(!ufatExtRead(sec, offset, 1, buf)) return false;
if(buf[0] == 0) break; //no more entries
if(buf[0] != 0xE5 && buf[0] != 0x2E){ //we process only non-deleted, non "." and ".." entries
if(!n--){ //we found it
if(name){
name[0] = (buf[0] == 0x05) ? 0xE5 : buf[0];
if(!ufatExtRead(sec, offset + 1, 10, name + 1)) return false;
}
if(flags){
if(!ufatExtRead(sec, offset + 0x0B, 1, flags)) return false;
}
if(id){
if(!ufatExtRead(sec, offset + 0x1A, 2, buf)) return false;
*id = ufatGetU16(buf, 0);
}
if(sz){
if(!ufatExtRead(sec, offset + 0x1C, 4, buf)) return false;
*sz = ufatGetU32(buf, 0);
}
return true;
}
}
offset += 32;
if(offset == UFAT_DISK_SECTOR_SZ){
offset = 0;
sec++;
}
}
//we fail
return false;
}
bool ufatOpen(u16 id){
curClus = id;
return true;
}
u16 ufatGetNextClus(u16 clus){
char buf[2];
u32 sec = diskOffset + fatSec;
u16 offset;
sec += clus / (UFAT_DISK_SECTOR_SZ / 2);
offset = (clus % (UFAT_DISK_SECTOR_SZ / 2)) * 2;
if(!ufatExtRead(sec, offset, 2, buf)) return CLUS_INVALID;
clus = ufatGetU16(buf, 0);
if(clus >= EOC_16) return CLUS_INVALID;
return clus;
}
bool ufatGetNextSectorRange(u32* first, u32* len){
u16 next = curClus, prev;
u32 t;
if(curClus == CLUS_INVALID) return false;
do{
prev = next;
next = ufatGetNextClus(prev);
}while(next == prev + 1 && next != CLUS_INVALID);
//prev is now the last cluster in this chain that is in sequence with previous ones
//next is now the next cluster (not in sequence - fragment)
t = prev + 1 - curClus;
t *= secPerClus;
*len = t;
t = (curClus - 2);
t *= secPerClus;
t += dataSec;
t += diskOffset;
*first = t;
curClus = next;
return true;
}
Código: Seleccionar todo
#ifndef _UFAT_H_
#define _UFAT_H_
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
typedef unsigned char bool;
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#ifndef NULL
#define NULL ((void*)0)
#endif
#define UFAT_DISK_SECTOR_SZ 512
#define UFAT_FLAG_READONLY 1
#define UFAT_FLAG_HIDDEN 2
#define UFAT_FLAG_SYSTEM 4
#define UFAT_FLAG_DIR 16
#define UFAT_FLAG_ARCHIVE 32
//externally required function(s)
bool ufatExtRead(u32 sector, u16 offset, u8 len, u8* buf);
void ufatInit(void); //init fs driver
bool ufatMount(void); //try mounting a volume
bool ufatGetNthFile(u16 n, char* name, u32* sz, u8* flags, u16* id); //in root directory only, false for no more
bool ufatOpen(u16 id); //in root directory only
bool ufatGetNextSectorRange(u32* first, u32* len); //for currently opened file, false for "no more"
#endif
Código: Seleccionar todo
#include "common.h"
#include "ufat/ufat.h"
#include "SD.h"
__CONFIG(FOSC_INTOSC & WDTE_SWDTEN & PWRTE_OFF & MCLRE_OFF & CP_OFF & CPD_OFF & BOREN_OFF & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF);
__CONFIG(WRT_OFF & PLLEN_OFF & STVREN_ON & BORV_19 & LVP_OFF);
__IDLOC(0000);
UInt8 eeRead(UInt8 addr){
EECON1 = 0b00000000; //read data
EEADRL = addr;
EECON1bits.RD = 1; //do it
return EEDATL;
}
void eeWrite(UInt8 addr, UInt8 data){
EECON1= 0b00000100; //write data
EEADRL = addr;
EEDATL = data;
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
while(EECON1bits.WR);
}
static UInt16 rnd(void){
UInt32 x;
x = eeRead(0xFF);
x = (x << 8) | eeRead(0xFE);
x = (x << 8) | eeRead(0xFD);
x = (x << 8) | eeRead(0xFC);
x = x * 0xDEECE66D + 0x0B;
eeWrite(0xFF, x >> 24);
eeWrite(0xFE, x >> 16);
eeWrite(0xFD, x >> 8);
eeWrite(0xFC, x >> 0);
return x >> 16;
}
void log(UInt8 val){
static UInt8 addr = 0;
eeWrite(addr++, val);
while(!addr);
}
#define BUF_SZ 90
#define MAX_SECTOR_RANGES 50 // = (EEPROM_SIZE - 4/*for random number generator*/) / 5
#define CP1_CON_VAL 0b00001100
static UInt8 gRead = 0;
static UInt8 gWrite = 1;
static UInt8 gBuffer[BUF_SZ];
static volatile UInt8 tmrReload;
static UInt8 byteStride; //for non-8-bit-per-sample-mono
void secListRead(UInt8 which, UInt24* start, UInt16* len){
UInt24 t24;
UInt16 t16;
UInt8 i;
if(which >= MAX_SECTOR_RANGES){
*start = 0;
*len = 0;
}
which = which + (which << 2); // which *= 5 :)
t24 = 0;
for(i = 0; i < 3; i++) t24 = (t24 << 8) | eeRead(which++);
*start = t24;
t16 = 0;
for(i = 0; i < 2; i++) t16 = (t16 << 8) | eeRead(which++);
*len = t16;
}
void secListWrite(UInt8 which, UInt24 start, UInt16 len){
UInt8 i;
if(which >= MAX_SECTOR_RANGES) return;
which = which + (which << 2); // which *= 5 :)
for(i = 0; i < 3; i++, start >>= 8) eeWrite(which + (2 - i), start);
for(i = 0; i < 2; i++, len >>= 8) eeWrite(which + (4 - i), len);
}
void fatal(UInt8 val){ //fatal error: beep error number a few times, then go to sleep
UInt8 i, j, k;
for(j = 0; j < 5; j++){
for(k = 0; k < val; k++){
__delay_ms(300);
for(i = 0; i < 100; i++){
RA5 = 1;
__delay_ms(1);
RA5 = 0;
__delay_ms(1);
}
}
__delay_ms(3000);
}
RA0 = 0; //SD off
while(1){
#asm
SLEEP
#endasm
}
}
void audioOn(void){
UInt8 i;
for(i = 0; i < BUF_SZ; i++) gBuffer[i] = 0x80;
T2CON = 0b00000100; //timer2 on
PR2 = 31; //should be 7, but that gives bad waveforms so we sacrifice some volume for better reproduction. 15 works beter, 31 best
CCP1CON = CP1_CON_VAL; //PWM on
CCPR1H = 0;
CCPR1L = 0;
TRISAbits.TRISA5 = 0; //pin is output
TMR0 = 0;
INTCONbits.TMR0IF = 0;
INTCONbits.TMR0IE = 1;
}
void audioOff(void){
INTCONbits.TMR0IE = 0;
CCP1CON = 0;
TRISAbits.TRISA5 = 0; //pin is output
RA5 = 0;
}
void sleep(UInt32 ms){ //sleeps unconditionally and approximately this many milliseconds...
#define HIGHEST_WDT_BIT 18
Int8 i;
UInt8 oldClkCfg, oldTris;
const UInt32 v_initializer = 1UL << HIGHEST_WDT_BIT; //bc this compiler sucks
UInt32 v = v_initializer;
oldClkCfg = OSCCON;
OSCCON = 0b00010011; //switch to slower clock
RA5 = 0;
oldTris = TRISA;
TRISA = 0b11011110; //all in except RA5 and RA0
RA0 = 0;
for(i = HIGHEST_WDT_BIT; i >= 0; i--, v >>= 1){
while(ms >= v){
#asm
CLRWDT
#endasm
WDTCON = (i << 1) | 1;
ms -= v;
#asm
SLEEP
#endasm
}
}
SWDTEN = 0;
TRISA = oldTris;
OSCCON = oldClkCfg; //switch to 31KHz clock
}
bool ufatExtRead(u32 sector, u16 offset, u8 len, u8* buf){
static u32 curSec = 0xFFFFFFFFUL;
static u16 curPos = 0;
if(sector != curSec || offset < curPos){
if(curSec != 0xFFFFFFFFUL){
while(curPos++ != 512) sdSpiByte(0xFF); //fast forward to sector end
sdSecReadStop();
curSec = 0xFFFFFFFFUL;
}
if(!sdReadStart(curSec = sector)) return false;
curPos = 0;
}
while(curPos != offset){ //skip to where we're needed
curPos++;
sdSpiByte(0xFF);
}
curPos += len;
while(len--) *buf++ = sdSpiByte(0xFF);
if(curPos == 512){
sdSecReadStop();
curSec = 0xFFFFFFFFUL;
}
return true;
}
void ufatExtReadTerminate(void){
//finish reading whatever sector we're reading so that we can issue commands to the SD card
ufatExtRead(0, 512, 0, NULL);
}
static UInt8 byte(){
return sdSpiByte(0xFF);
}
static Boolean spiCmp(const char* with, UInt8 len){ //return true for match, false otherwise
while(len--) if(byte() != *with++) return false;
return true;
}
static UInt8 hdrProcess(){ //read at most 127 bytes and use them, return number read. return zero if file is invalid
static const UInt8 riff[4] = {'R', 'I', 'F', 'F'};
static const UInt8 wave[4] = {'W', 'A', 'V', 'E'};
UInt8 csz, i = 0, j;
UInt32 t32;
//check for RIFF header
if(!spiCmp(riff, 4)) return 0; //no RIFF header -> not MS RIFF media format -> fail
//skip file size
for(j = 0; j < 4; j++) byte();
//check for WAVE format header
if(!spiCmp(wave, 4)) return 0; //no WAVE type identifier header -> not WAVE format -> fail
i += 12;
while(i < 127){ //look for "fmt "header in the first 127 bytes
UInt8 hdr[4];
for(j = 0; j < 4; j++) hdr[j] = byte(); //read chunk type
csz = byte(); //read chunk size
if(byte() || byte() || byte()) return 0; //chunk over 256 bytes? -> too big to skip
i += 8;
if(hdr[0] == 'f' && hdr[1] == 'm' && hdr[2] == 't' && hdr[3] == ' '){ //our lucky day -> it's the format chnk
byteStride = 1;
if(byte() != 1 || byte() != 0) return 0; //not PCM format -> fail
i += 2;
j = byte();
if(byte() != 0) return 0; //over 256 channels is not supported!
if(!j) return 0; //zero channels also not so good
byteStride *= j; //we only play the first channel
i += 2;
t32 = 0;
for(j = 0; j < 4; j++) t32 = (t32 >> 8) | (((UInt32)byte()) << 24); //read sample rate
i += 4;
#define DIV (4 * t32)
tmrReload = 69;
t32 = (_XTAL_FREQ + (DIV / 2)) / DIV;
//sz is now the delay we need. we need to split it equally between OPTION reg and tmrReload
j = 0;
while(t32 > 256){
j++;
t32 >>= 1;
}
if(j >= 8) return 0; //too slow...
OPTION_REG = (OPTION_REG & 0xF0) | (j ? (j - 1) : 8);
tmrReload = 256 - t32;
for(j = 0; j < 6; j++) sdSpiByte(0xFF); //skip byte rate and clock align
i += 6;
if(sdSpiByte(0xFF) != 8 || sdSpiByte(0xFF) != 0) return 0; //8 bits per sample only please
i += 2;
byteStride--;
return i;
}
else{ //skip this chunk and go on
if(csz > 0x80) return 0; //too big a chunk to skip -> give up
i += csz;
while(csz--) byte();
}
}
return i;
}
static void play(){
Boolean ret, start = true;
UInt24 firstSec;
UInt16 numSec;
UInt24 sec;
UInt8 secListIdx = 0;
UInt8 i, t = 0;
audioOn();
while(1){
secListRead(secListIdx++, &firstSec, &numSec);
if(!firstSec && !numSec) break;
ret = sdReadStart(firstSec);
if(!ret) fatal(2);
if(start){ //first part of file is header -> get some useful info out of it
i = hdrProcess();
if(i == 0){
sdSecReadStop();
break; //file invalid -> fail
}
start = false;
} else i= 0;
for(sec = 0; sec < numSec; sec++){
UInt8 j;
if(sec){
i = 0;
sdNextSec();
}
for(j =0; j < 4; j++){ //faster than 16 bit counter to 512
if(j) i = 0;
while(i != 128){
if(t){
t--;
sdSpiByte(0xFF);
i++;
}
else if(gWrite != gRead){
t = sdSpiByte(0xFF);
gBuffer[gWrite++] = t >> 2;
if(gWrite == BUF_SZ) gWrite = 0;
i++;
t = byteStride;
}
}
}
}
sdSecReadStop();
}
audioOff();
}
UInt16 measureBattery(void){ //result in millivolts
UInt24 t = 0;
UInt8 i;
FVRCON = 0b11000001; //1.024V Vref on
ADCON0 = 0b01111101; //configure ADC to measure 0.6 v reference
ADCON1 = 0b11110000; //Convert using Vdd as ref
__delay_ms(1);
for(i = 0; i < 10; i++){
__delay_us(160);
ADCON0bits.GO_nDONE = 1;
while(ADCON0bits.GO_nDONE); //wait
t += ADRES;
}
FVRCON = 0b00000000; //Vref off
ADCON0 &=~ 0b00000001; //ADC off
/*
10*t = 1.024*10*1023/Vcc
Vcc = 10.24*1023/10*t
Vcc*1000=10240*1023/10*t
Vcc*1000=10475520/t
*/
t = (10475520 + (t >> 1)) / t;
return t;
}
void main(void){
Boolean ret;
char name[11];
UInt8 flags;
UInt16 n, i = 0, id, numFiles = 0;
UInt32 sec, sz;
OSCCON = 0b11110000; //32 MHz clock
OSCTUNE = 0x1F; //33 MHz clok really
INTCON = 0b10000000; //ints enabled, all sources masked
APFCON = 0b01000001; //MOSI on RA4, CCP1 on RA5
TRISA = 0b11001100; //out on 0, 1, 4, 5, in on 2, DNK on 3
ANSELA = 0; //no analog inputs
OPTION_REG = 0b10000000; //no weak pull-ups, timer0 at OSC/4/2 = osc/ 8
SWDTEN = 0; //WDT off for now
__delay_ms(64); //make sure EEPROM is ready for writes by the time we finish this
__delay_ms(500);
n = measureBattery();
if(n > 3600) fatal(10); //too high voltage -> unsafe to power on the sd card
if(n < 2500) fatal(11); //too low voltage -> SD card might be unstable
RA0 = 1; //card power on
ret = sdInit();
if(!ret) fatal(1);
ufatInit();
ret = ufatMount();
if(!ret) fatal(6);
while(ufatGetNthFile(i, name, &sz, &flags, &id)){
i++;
if(flags & (UFAT_FLAG_DIR | UFAT_FLAG_HIDDEN)) continue; //skip dirs and hidden files
if(name[8] !='W' || name[9] != 'A' || name[10] != 'V') continue; //skip non-WAV files
numFiles++;
}
if(!numFiles) fatal(3); //no files
while(1){
UInt8 j;
n = rnd() % numFiles; //pick a random file
i = 0;
while(ufatGetNthFile(i, name, &sz, &flags, &id)){
i++;
if(flags & (UFAT_FLAG_DIR | UFAT_FLAG_HIDDEN)) continue; //skip dirs and hidden files
if(name[8] !='W' || name[9] != 'A' || name[10] != 'V') continue; //skip non-WAV files
if(!n--) goto found;
}
fatal(9);
found:
if(!ufatOpen(id)) fatal(7);
j = 0;
while(ufatGetNextSectorRange(&sec, &sz)){
while(sz){
UInt16 sv = sz > 65535 ? 65535 : sz;
secListWrite(j++, sec, sv);
sec += sv;
sz -= sv;
}
}
secListWrite(j, 0, 0);
ufatExtReadTerminate();
play();
RA0 = 0; //card off
sleep(30000);
RA0 = 1; //card power on
ret = sdInit();
if(!ret) fatal(1);
}
while(1);
}
void interrupt isr(void){
// if(INTCONbits.TMR0IF){ //not needed
UInt8 v, v1;
TMR0 = tmrReload;
v = gBuffer[gRead++];
v1 = (CP1_CON_VAL & 0xCF) | ((v & 3) << 4);
v = v >> 2;
CCPR1L = v;
CCP1CON = v1;
if(gRead == BUF_SZ) gRead = 0;
INTCONbits.TMR0IF = 0;
// }
}
Código: Seleccionar todo
#include "SD.h"
#define FLAG_TIMEOUT 0x80
#define FLAG_PARAM_ERR 0x40
#define FLAG_ADDR_ERR 0x20
#define FLAG_ERZ_SEQ_ERR 0x10
#define FLAG_CMD_CRC_ERR 0x08
#define FLAG_ILLEGAL_CMD 0x04
#define FLAG_ERZ_RST 0x02
#define FLAG_IN_IDLE_MODE 0x01
/*
this is a very simplified SD/MMC diver [which is adapted from PowerSDHC sources :) ]
it supports reads in stream mode. i removed support for block reads/writes and SDHC/MMCplus support since it is not needed.
also removed is ability to read card size since it is also unused (it saves us 300 instructions)
*/
static void sdClockSpeed(Boolean fast){
SSP1CON1 = (SSP1CON1 & 0b11110000) | (fast ? 0b0000 : 0b1010);
}
#define sdChipSelect(/*Boolean*/ active) //NO NEED! CS is active low on cards, btw
UInt8 sdSpiByte(UInt8 byte){
SSP1BUF = byte;
while(!SSP1STATbits.BF);
return SSP1BUF;
}
static void sdSpiInit(void){
SSP1ADD = 21; //slow clock will be 375KHz @33MHZ (363@32)
SSP1CON1 = 0b00101010; //spi master, clk=timer2, CPOL = 0
SSP1STAT = 0b11000000; //CPHA = 0
}
static void sdSpiResync(void){
LATA1 = 0; //CLK is low
LATA4 = 1; //MOSO is high
SSPEN = 0;
__delay_us(10);
LATA1 = 1; //CLK is high
__delay_us(10);
LATA1 = 0; //CLK is low
__delay_us(10);
SSPEN = 1;
}
static UInt8 sdCrc7(UInt8* chr,UInt8 cnt,UInt8 crc){
UInt8 i, a;
UInt8 Data;
for(a = 0; a < cnt; a++){
Data = chr[a];
for(i = 0; i < 8; i++){
crc <<= 1;
if( (Data & 0x80) ^ (crc & 0x80) ) crc ^= 0x09;
Data <<= 1;
}
}
return crc & 0x7F;
}
static inline void sdPrvSendCmd(UInt8 cmd, UInt32 param){
UInt8 send[6];
send[0] = cmd | 0x40;
send[1] = param >> 24;
send[2] = param >> 16;
send[3] = param >> 8;
send[4] = param;
send[5] = (sdCrc7(send, 5, 0) << 1) | 1;
for(cmd = 0; cmd < sizeof(send); cmd++){
sdSpiByte(send[cmd]);
}
}
static inline UInt8 sdPrvReadResp(void){ //if return has FLAG_TIMEOUT bit_ set, we timed out
UInt8 v, i = 0;
do{ //our max wait time is 128 byte clocks (1024 clock ticks)
v = sdSpiByte(0xFF);
}while(i++ < 128 && (v == 0xFF));
return v;
}
static UInt8 sdPrvSimpleCommand(UInt8 cmd, UInt32 param, Boolean cmdDone){ //do a command, return R1 reply
UInt8 ret;
sdChipSelect(true);
sdSpiByte(0xFF);
sdPrvSendCmd(cmd, param);
ret = sdPrvReadResp();
if(cmdDone) sdChipSelect(false);
return ret;
}
static UInt8 sdPrvACMD(UInt8 cmd, UInt32 param){
UInt8 ret;
ret = sdPrvSimpleCommand(55, 0, true);
if(ret & FLAG_TIMEOUT) return ret;
if(ret & FLAG_ILLEGAL_CMD) return ret;
return sdPrvSimpleCommand(cmd, param, true);
}
Boolean sdPrvCardInit(Boolean sd){
UInt16 time = 0;
UInt8 resp;
Boolean first = true;
UInt32 param;
param = 0;
while(time++ < 5000UL){ //retry 10..0 times
resp = sd ? sdPrvACMD(41, param) : sdPrvSimpleCommand(1, param, true);
if(resp & FLAG_TIMEOUT) break;
if(first){
param |= 0x00200000UL;
first = false;
}
else{
if(!(resp & FLAG_IN_IDLE_MODE)) return true;
}
}
return false;
}
UInt32 sdPrvGetBits(UInt8* data, UInt8 numBytesInArray, UInt8 startBit, UInt8 len){//for CID and CSD data..
UInt8 bitWrite = 0;
UInt8 numBitsInArray = numBytesInArray * 8;
UInt32 ret = 0;
do{
UInt8 bit_,byte;
bit_ = numBitsInArray - startBit - 1;
byte = bit_ >> 3;
bit_ = 7 - (bit_ & 7);
ret |= ((data[byte] >> bit_) & 1) << (bitWrite++);
startBit++;
}while(--len);
return ret;
}
Boolean sdInit(){
UInt8 v, tries = 0;
Boolean SD;
SD = false;
sdSpiInit();
sdClockSpeed(false);
sdChipSelect(false);
for(v = 0; v < 20; v++) sdSpiByte(0xFF); //lots of clocks with CS not asserted to give card time to init
//with CS tied low, we get here with clock sync a bit weird, so we need to re-sync it, we do so here, since we know for sure what the valid RESP for CMD0 is
do{
sdSpiResync();
v = sdPrvSimpleCommand(0, 0, true);
//resync usage makes this bad, so i comment it out: if(v & FLAG_TIMEOUT) return false;
tries++;
if(tries > 30) return false;
}while(v != 0x01);
v = sdPrvSimpleCommand(55, 0, true); //see if this is SD or MMC
if(v & FLAG_TIMEOUT) return false;
SD = !(v & FLAG_ILLEGAL_CMD);
if(!sdPrvCardInit(SD))return false;
v = sdPrvSimpleCommand(16, 512, true); //sec sector size
if(v) return false;
v = sdPrvSimpleCommand(59, 0, true); //crc off
if(v) return false;
sdClockSpeed(true);
return true;
}
//stream mode
Boolean sdReadStart(UInt24 sec){
UInt8 v;
v = sdPrvSimpleCommand(18, ((UInt32)sec) << 9, false);
if(v & FLAG_TIMEOUT) {log(0xEF); return false; }
do{
v = sdSpiByte(0xFF);
}while(v == 0xFF);
if(v != 0xFE) {log(0xEE); return false;}
return true;
}
void sdNextSec(){
UInt8 v;
sdSpiByte(0xFF); //skip crc
sdSpiByte(0xFF);
do{
v = sdSpiByte(0xFF);
}while(v == 0xFF);
}
void sdSecReadStop(){
//cancel read
sdPrvSimpleCommand(12, 0, true);
}
Código: Seleccionar todo
#ifndef _COMMON_H_
#define _COMMON_H_
#include <htc.h>
#define _XTAL_FREQ 33000000 //with osctune maxed out
typedef signed char Int8;
typedef unsigned char UInt8;
typedef unsigned char Boolean;
typedef unsigned long UInt32;
typedef unsigned short long UInt24;
typedef unsigned short UInt16;
#define inline
#define true 1
#define false 0
#define NULL ((void*)0)
void log(UInt8);
//RA2 = MISO
//RA1 = CLK
//RA4 = MOSI
//RA0 = card power
#endif
Código: Seleccionar todo
#ifndef _SD_H_
#define _SD_H_
#include "common.h"
#define SD_BLOCK_SIZE 512
Boolean sdInit();
UInt8 sdSpiByte(UInt8 byte);
Boolean sdReadStart(UInt24 sec);
void sdNextSec();
void sdSecReadStop();
#endif