Reproductor de sonido WAV simple con PIC y SD

Sección dedicada a imágenes de circuitos, diagramas que no requieren explicación previa, Proyectos sencillos.
Area para desarrollo de proyectos, colaboración de toda la comunidad en proyectos. Solución de dudas y aportes.

No publicar manuales, no publicar temas ajenos a los proyectos. de ser necesario, hacer indicación con enlace entre etiquetas CODE
Avatar de Usuario
Enigma
Administrador del Sitio
Administrador del Sitio
Mensajes: 1268
Registrado: 20 Oct 2013, 16:26
Genero: Mujer
Profesion: Webmaster CEO SEO
Ubicacion: Matrix
Navegador: Chrome
Contactar:

Reproductor de sonido WAV simple con PIC y SD

Mensaje sin leer por Enigma » 05 Oct 2021, 11:52

Reproductor de sonido WAV simple con PIC y SD

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).

Imagen

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.

Imagen

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
Librerías uFat ufat.c / ufat.h

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;
}
uFatH

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
Main.C

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;
//	}
}
SD.C

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);
}

common.h

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
SD.h

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



Volver a “Proyectos y Circuitos Didacticos”