├── .gitignore ├── .gitmodules ├── Makefile ├── encoder.c ├── encoder.h ├── leds.c ├── leds.h ├── main.c ├── mass-storage ├── LUFAConfig.h ├── descriptors.c ├── descriptors.h ├── mass_storage.c ├── mass_storage.h ├── scsi.c └── scsi.h ├── mp3.c ├── mp3.h ├── player.c ├── player.h ├── readme.md ├── sd-reader ├── FAQ ├── byteordering.c ├── byteordering.h ├── fat.c ├── fat.h ├── fat_config.h ├── partition.c ├── partition.h ├── partition_config.h ├── sd-reader_config.h ├── sd_manager.c ├── sd_manager.h ├── sd_raw.c ├── sd_raw.h └── sd_raw_config.h ├── sd.c ├── sd.h ├── serial.c ├── serial.h ├── tenths.c └── tenths.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.lss 3 | *.map 4 | *.sym 5 | *.hex 6 | *.bin 7 | *.eep 8 | *.elf 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lufa"] 2 | path = lufa 3 | url = https://github.com/abcminiuser/lufa 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MCU = atmega32u4 2 | ARCH = AVR8 3 | F_CPU = 8000000 4 | F_USB = $(F_CPU) 5 | OPTIMIZATION = s 6 | TARGET = fw 7 | SRC = encoder.c leds.c main.c mp3.c player.c sd.c serial.c tenths.c \ 8 | sd-reader/byteordering.c sd-reader/fat.c sd-reader/partition.c \ 9 | sd-reader/sd_raw.c sd-reader/sd_manager.c \ 10 | mass-storage/descriptors.c mass-storage/mass_storage.c \ 11 | mass-storage/scsi.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) $(LUFA_SRC_SERIAL) 12 | 13 | LUFA_PATH = lufa/LUFA 14 | CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -Imass-storage 15 | 16 | all: 17 | 18 | include $(LUFA_PATH)/Build/lufa_core.mk 19 | include $(LUFA_PATH)/Build/lufa_sources.mk 20 | include $(LUFA_PATH)/Build/lufa_build.mk 21 | 22 | dfu: all 23 | dfu-programmer $(MCU) erase 24 | dfu-programmer $(MCU) flash --suppress-bootloader-mem $(TARGET).hex 25 | 26 | fuse: 27 | avrdude -p $(MCU) -c usbtiny -U lfuse:w:0xde:m -U hfuse:w:0xd9:m -U efuse:w:0xfb:m 28 | -------------------------------------------------------------------------------- /encoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "encoder.h" 6 | 7 | volatile int encoder=0; 8 | 9 | void encoder_bootloader_check(void) 10 | { 11 | // Turn on encoder switch pull-up 12 | PORTB |= (1 << PB6); 13 | 14 | // Wait a little bit for values to settle 15 | _delay_ms(100); 16 | 17 | // Then jump to the bootloader if the switch is presed. 18 | if (encoder_switch) __asm("jmp 0x7000"); 19 | } 20 | 21 | void encoder_init(void) 22 | { 23 | // Set internal pull-ups to on 24 | PORTB |= (1 << PB5); // encoder B 25 | PORTB |= (1 << PB6); // encoder switch 26 | PORTC |= (1 << PC6); // encoder A 27 | 28 | // Prepare timer3 for use in debouncing. 29 | // (running at 1 MHz and interrupting at 3906 KHz) 30 | OCR3AH = 0x00; 31 | OCR3AL = 0xff; 32 | TIMSK3 |= (1 << OCIE3A); 33 | TCCR3B |= (1 << CS31); 34 | } 35 | 36 | void encoder_clear(void) 37 | { 38 | TIMSK3 &= ~(1 << OCIE3A); 39 | encoder = 0; 40 | TIMSK3 |= (1 << OCIE3A); 41 | } 42 | 43 | static volatile uint8_t current_state = 0; 44 | static volatile uint8_t next_state = 0; 45 | static volatile uint8_t count = 0; 46 | 47 | ISR(TIMER3_COMPA_vect) 48 | { 49 | uint8_t state = ((PINB & (1 << PB5)) ? 1 : 0) | 50 | ((PINC & (1 << PC6)) ? 2 : 0); 51 | 52 | if (state == current_state) 53 | { 54 | ; 55 | } 56 | else if (state == next_state) 57 | { 58 | if (++count == 10) 59 | { 60 | if (current_state == 0 && next_state == 2) 61 | encoder++; 62 | else if (current_state == 0 && next_state == 1) 63 | encoder--; 64 | current_state = next_state; 65 | } 66 | } 67 | else 68 | { 69 | next_state = state; 70 | count = 0; 71 | } 72 | TCNT3H = 0; 73 | TCNT3L = 0; 74 | } 75 | -------------------------------------------------------------------------------- /encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef ENCODER_H 2 | #define ENCODER_H 3 | 4 | #include 5 | 6 | // The encoder is defined by two values: 7 | // encoder is an int representing ticks in either direction 8 | // encoder_switch is a macro to check the desired pin 9 | extern volatile int encoder; 10 | #define encoder_switch (!(PINB & (1 << PB6))) 11 | 12 | void encoder_bootloader_check(void); 13 | void encoder_init(void); 14 | void encoder_clear(void); 15 | #endif 16 | -------------------------------------------------------------------------------- /leds.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "leds.h" 6 | #include "tenths.h" 7 | 8 | volatile uint8_t LEDs[8]; // Values from 0 to 7, with 7 being brightest 9 | 10 | void LEDs_init() 11 | { 12 | // Shift register clock, storage register clock, output enable 13 | // are all outputs. 14 | DDRD |= (1 << PD4) | (1 << PD6) | (1 << PD7); 15 | 16 | // Serial data input is also an output. 17 | DDRB |= (1 << PB4); 18 | 19 | // Set output enable to 0 (enables outputs on shift register) 20 | PORTD &= ~(1 << PD7); 21 | 22 | // Enable output compare interrupt 23 | TIMSK0 |= (1 << OCIE0A); 24 | 25 | // Turn on timer0 with 256x prescalar 26 | TCCR0B |= (1 << CS02); 27 | 28 | for (int i=0; i < 8; ++i) LEDs[i] = 0; 29 | } 30 | 31 | void LEDs_volume(bool bright, uint8_t volume) 32 | { 33 | for (uint8_t i=0; i < 8; ++i) 34 | { 35 | if (i+1 <= volume) LEDs[i] = bright ? 4 : 2; 36 | else LEDs[i] = 0; 37 | } 38 | } 39 | 40 | void LEDs_sleep() 41 | { 42 | for (int i=0; i < 7; ++i) LEDs[i] = 0; 43 | LEDs[7] = 1; 44 | } 45 | 46 | void LEDs_next() 47 | { 48 | uint8_t buffer[24]; 49 | for (int i=0; i < 8; ++i) 50 | { 51 | buffer[i] = LEDs[i]; 52 | buffer[i + 8] = 0; 53 | buffer[i + 16] = LEDs[i]; 54 | } 55 | 56 | for (int i=0; i < 16; ++i) 57 | { 58 | for (int j=0; j < 8; ++j) LEDs[j] = buffer[i+j]; 59 | _delay_ms(20); 60 | } 61 | } 62 | 63 | void LEDs_prev() 64 | { 65 | uint8_t buffer[24]; 66 | for (int i=0; i < 8; ++i) 67 | { 68 | buffer[i] = LEDs[i]; 69 | buffer[i + 8] = 0; 70 | buffer[i + 16] = LEDs[i]; 71 | } 72 | 73 | for (int i=15; i >= 0; --i) 74 | { 75 | for (int j=0; j < 8; ++j) LEDs[j] = buffer[i+j]; 76 | _delay_ms(20); 77 | } 78 | } 79 | 80 | void LEDs_usb() 81 | { 82 | unsigned t = tenths % 14; 83 | if (t >= 8) 84 | t = 14 - t; 85 | 86 | for (int i=0; i < 8; ++i) 87 | LEDs[i] = (i == t) ? 5 : 0; 88 | } 89 | 90 | //////////////////////////////////////////////////////////////////////////////// 91 | 92 | volatile uint8_t tick = 0; // Goes from 0 to 7. 93 | uint8_t levels[8] = { 94 | 0, 10, 20, 35, 55, 80, 110, 145 95 | }; 96 | 97 | void update_LEDs(void) 98 | { 99 | for (int i=0; i < 8; ++i) 100 | { 101 | if (LEDs[i] > tick) { 102 | PORTB |= (1 << PB4); 103 | } else { 104 | PORTB &= ~(1 << PB4); 105 | } 106 | 107 | // Pulse the clock (SHCP) to send this bit of data 108 | PORTD |= (1 << PD4); 109 | PORTD &= ~(1 << PD4); 110 | } 111 | 112 | // Toggle STCP high and low to shift data to output stage 113 | PORTD |= (1 << PD6); 114 | PORTD &= ~(1 << PD6); 115 | } 116 | 117 | 118 | ISR(TIMER0_COMPA_vect) 119 | { 120 | update_LEDs(); 121 | tick = (tick + 1) % 8; 122 | OCR0A = levels[tick]; 123 | } 124 | -------------------------------------------------------------------------------- /leds.h: -------------------------------------------------------------------------------- 1 | #ifndef LEDS_H 2 | #define LEDS_H 3 | 4 | #include 5 | #include 6 | 7 | volatile extern uint8_t LEDs[8]; 8 | void LEDs_init(void); 9 | void LEDs_volume(bool bright, uint8_t volume); 10 | void LEDs_sleep(void); 11 | void LEDs_next(void); 12 | void LEDs_prev(void); 13 | 14 | void LEDs_usb(void); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "encoder.h" 8 | #include "serial.h" 9 | #include "leds.h" 10 | #include "mass_storage.h" 11 | #include "mp3.h" 12 | #include "sd.h" 13 | #include "tenths.h" 14 | #include "player.h" 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | int main(void) 19 | { 20 | // If the encoder switch is pressed on startup, jump to DFU bootloader. 21 | encoder_bootloader_check(); 22 | 23 | // Initialize all of the peripherals 24 | encoder_init(); 25 | LEDs_init(); 26 | serial_init(); 27 | tenths_init(); 28 | 29 | mass_storage_init(); 30 | sei(); 31 | 32 | LEDs[0] = 5; 33 | while (!sd_init()) 34 | _delay_ms(100); 35 | 36 | LEDs[1] = 5; 37 | while (!mp3_init()) 38 | _delay_ms(100); 39 | 40 | // Initialize player state 41 | player_init(); 42 | 43 | while (1) 44 | { 45 | if (usb_task()) 46 | { 47 | LEDs_usb(); 48 | } 49 | else 50 | { 51 | player_manage_buffer(); 52 | player_update_state(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mass-storage/LUFAConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | Based on code from the LUFA library, with the following copyright 3 | 4 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 5 | 6 | Permission to use, copy, modify, distribute, and sell this 7 | software and its documentation for any purpose is hereby granted 8 | without fee, provided that the above copyright notice appear in 9 | all copies and that both that the copyright notice and this 10 | permission notice and warranty disclaimer appear in supporting 11 | documentation, and that the name of the author not be used in 12 | advertising or publicity pertaining to distribution of the 13 | software without specific, written prior permission. 14 | 15 | The author disclaims all warranties with regard to this 16 | software, including all implied warranties of merchantability 17 | and fitness. In no event shall the author be liable for any 18 | special, indirect or consequential damages or any damages 19 | whatsoever resulting from loss of use, data or profits, whether 20 | in an action of contract, negligence or other tortious action, 21 | arising out of or in connection with the use or performance of 22 | this software. 23 | */ 24 | #ifndef LUFA_CONFIG_H 25 | #define LUFA_CONFIG_H 26 | 27 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_DISABLED | USB_OPT_AUTO_PLL) 28 | #define USB_DEVICE_ONLY 29 | 30 | #define USE_FLASH_DESCRIPTORS 31 | #define FIXED_CONTROL_ENDPOINT_SIZE 8 32 | #define FIXED_NUM_CONFIGURATIONS 1 33 | #define INTERRUPT_CONTROL_ENDPOINT 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /mass-storage/descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2014. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * USB Device Descriptors, for library use when in USB device mode. Descriptors are special 34 | * computer-readable structures which the host requests upon device enumeration, to determine 35 | * the device's capabilities and functions. 36 | */ 37 | 38 | #include "descriptors.h" 39 | 40 | 41 | /** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall 42 | * device characteristics, including the supported USB version, control endpoint size and the 43 | * number of device configurations. The descriptor is read out by the USB host when the enumeration 44 | * process begins. 45 | */ 46 | const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = 47 | { 48 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 49 | 50 | .USBSpecification = VERSION_BCD(1,1,0), 51 | .Class = USB_CSCP_NoDeviceClass, 52 | .SubClass = USB_CSCP_NoDeviceSubclass, 53 | .Protocol = USB_CSCP_NoDeviceProtocol, 54 | 55 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 56 | 57 | .VendorID = 0x03EB, 58 | .ProductID = 0x2045, 59 | .ReleaseNumber = VERSION_BCD(0,0,1), 60 | 61 | .ManufacturerStrIndex = STRING_ID_Manufacturer, 62 | .ProductStrIndex = STRING_ID_Product, 63 | .SerialNumStrIndex = USE_INTERNAL_SERIAL, 64 | 65 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 66 | }; 67 | 68 | /** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage 69 | * of the device in one of its supported configurations, including information about any device interfaces 70 | * and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting 71 | * a configuration so that the host may correctly communicate with the USB device. 72 | */ 73 | const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = 74 | { 75 | .Config = 76 | { 77 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 78 | 79 | .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), 80 | .TotalInterfaces = 1, 81 | 82 | .ConfigurationNumber = 1, 83 | .ConfigurationStrIndex = NO_DESCRIPTOR, 84 | 85 | .ConfigAttributes = USB_CONFIG_ATTR_RESERVED, 86 | 87 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) 88 | }, 89 | 90 | .MS_Interface = 91 | { 92 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 93 | 94 | .InterfaceNumber = INTERFACE_ID_MassStorage, 95 | .AlternateSetting = 0, 96 | 97 | .TotalEndpoints = 2, 98 | 99 | .Class = MS_CSCP_MassStorageClass, 100 | .SubClass = MS_CSCP_SCSITransparentSubclass, 101 | .Protocol = MS_CSCP_BulkOnlyTransportProtocol, 102 | 103 | .InterfaceStrIndex = NO_DESCRIPTOR 104 | }, 105 | 106 | .MS_DataInEndpoint = 107 | { 108 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 109 | 110 | .EndpointAddress = MASS_STORAGE_IN_EPADDR, 111 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 112 | .EndpointSize = MASS_STORAGE_IO_EPSIZE, 113 | .PollingIntervalMS = 0x05 114 | }, 115 | 116 | .MS_DataOutEndpoint = 117 | { 118 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 119 | 120 | .EndpointAddress = MASS_STORAGE_OUT_EPADDR, 121 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 122 | .EndpointSize = MASS_STORAGE_IO_EPSIZE, 123 | .PollingIntervalMS = 0x05 124 | } 125 | }; 126 | 127 | /** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests 128 | * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate 129 | * via the language ID table available at USB.org what languages the device supports for its string descriptors. 130 | */ 131 | const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); 132 | 133 | /** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable 134 | * form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 135 | * Descriptor. 136 | */ 137 | const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"Matt Keeter"); 138 | 139 | /** Product descriptor string. This is a Unicode string containing the product's details in human readable form, 140 | * and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 141 | * Descriptor. 142 | */ 143 | const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"Bumpy"); 144 | 145 | /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" 146 | * documentation) by the application code so that the address and size of a requested descriptor can be given 147 | * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function 148 | * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the 149 | * USB host. 150 | */ 151 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 152 | const uint8_t wIndex, 153 | const void** const DescriptorAddress) 154 | { 155 | const uint8_t DescriptorType = (wValue >> 8); 156 | const uint8_t DescriptorNumber = (wValue & 0xFF); 157 | 158 | const void* Address = NULL; 159 | uint16_t Size = NO_DESCRIPTOR; 160 | 161 | switch (DescriptorType) 162 | { 163 | case DTYPE_Device: 164 | Address = &DeviceDescriptor; 165 | Size = sizeof(USB_Descriptor_Device_t); 166 | break; 167 | case DTYPE_Configuration: 168 | Address = &ConfigurationDescriptor; 169 | Size = sizeof(USB_Descriptor_Configuration_t); 170 | break; 171 | case DTYPE_String: 172 | switch (DescriptorNumber) 173 | { 174 | case STRING_ID_Language: 175 | Address = &LanguageString; 176 | Size = pgm_read_byte(&LanguageString.Header.Size); 177 | break; 178 | case STRING_ID_Manufacturer: 179 | Address = &ManufacturerString; 180 | Size = pgm_read_byte(&ManufacturerString.Header.Size); 181 | break; 182 | case STRING_ID_Product: 183 | Address = &ProductString; 184 | Size = pgm_read_byte(&ProductString.Header.Size); 185 | break; 186 | } 187 | 188 | break; 189 | } 190 | 191 | *DescriptorAddress = Address; 192 | return Size; 193 | } 194 | 195 | -------------------------------------------------------------------------------- /mass-storage/descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2014. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for Descriptors.c. 34 | */ 35 | 36 | #ifndef _DESCRIPTORS_H_ 37 | #define _DESCRIPTORS_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | #include 42 | 43 | /* Macros: */ 44 | /** Endpoint address of the Mass Storage device-to-host data IN endpoint. */ 45 | #define MASS_STORAGE_IN_EPADDR (ENDPOINT_DIR_IN | 3) 46 | 47 | /** Endpoint address of the Mass Storage host-to-device data OUT endpoint. */ 48 | #define MASS_STORAGE_OUT_EPADDR (ENDPOINT_DIR_OUT | 4) 49 | 50 | /** Size in bytes of the Mass Storage data endpoints. */ 51 | #define MASS_STORAGE_IO_EPSIZE 64 52 | 53 | /* Type Defines: */ 54 | /** Type define for the device configuration descriptor structure. This must be defined in the 55 | * application code, as the configuration descriptor contains several sub-descriptors which 56 | * vary between devices, and which describe the device's usage to the host. 57 | */ 58 | typedef struct 59 | { 60 | USB_Descriptor_Configuration_Header_t Config; 61 | 62 | // Mass Storage Interface 63 | USB_Descriptor_Interface_t MS_Interface; 64 | USB_Descriptor_Endpoint_t MS_DataInEndpoint; 65 | USB_Descriptor_Endpoint_t MS_DataOutEndpoint; 66 | } USB_Descriptor_Configuration_t; 67 | 68 | /** Enum for the device interface descriptor IDs within the device. Each interface descriptor 69 | * should have a unique ID index associated with it, which can be used to refer to the 70 | * interface from other descriptors. 71 | */ 72 | enum InterfaceDescriptors_t 73 | { 74 | INTERFACE_ID_MassStorage = 0, /**< Mass storage interface descriptor ID */ 75 | }; 76 | 77 | /** Enum for the device string descriptor IDs within the device. Each string descriptor should 78 | * have a unique ID index associated with it, which can be used to refer to the string from 79 | * other descriptors. 80 | */ 81 | enum StringDescriptors_t 82 | { 83 | STRING_ID_Language = 0, /**< Supported Languages string descriptor ID (must be zero) */ 84 | STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */ 85 | STRING_ID_Product = 2, /**< Product string ID */ 86 | }; 87 | 88 | /* Function Prototypes: */ 89 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 90 | const uint8_t wIndex, 91 | const void** const DescriptorAddress) 92 | ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 93 | 94 | #endif 95 | 96 | -------------------------------------------------------------------------------- /mass-storage/mass_storage.c: -------------------------------------------------------------------------------- 1 | #include "descriptors.h" 2 | #include "scsi.h" 3 | 4 | #include "sd-reader/sd_raw.h" 5 | #include "player.h" 6 | 7 | /** LUFA Mass Storage Class driver interface configuration and state information. This structure is 8 | * passed to all Mass Storage Class driver functions, so that multiple instances of the same class 9 | * within a device can be differentiated from one another. 10 | */ 11 | USB_ClassInfo_MS_Device_t Disk_MS_Interface = 12 | { 13 | .Config = 14 | { 15 | .InterfaceNumber = INTERFACE_ID_MassStorage, 16 | .DataINEndpoint = 17 | { 18 | .Address = MASS_STORAGE_IN_EPADDR, 19 | .Size = MASS_STORAGE_IO_EPSIZE, 20 | .Banks = 1, 21 | }, 22 | .DataOUTEndpoint = 23 | { 24 | .Address = MASS_STORAGE_OUT_EPADDR, 25 | .Size = MASS_STORAGE_IO_EPSIZE, 26 | .Banks = 1, 27 | }, 28 | .TotalLUNs = TOTAL_LUNS, 29 | }, 30 | }; 31 | 32 | static bool ejected = false; 33 | 34 | void mass_storage_init(void) 35 | { 36 | USB_Init(); 37 | } 38 | 39 | void mass_storage_eject(void) 40 | { 41 | sd_raw_sync(); 42 | ejected = true; 43 | USB_Detach(); 44 | player_redraw(); 45 | } 46 | 47 | bool usb_task(void) 48 | { 49 | if (ejected && USB_DeviceState == DEVICE_STATE_Unattached) 50 | { 51 | USB_Attach(); 52 | ejected = false; 53 | } 54 | 55 | if (USB_DeviceState != DEVICE_STATE_Configured) 56 | return false; 57 | 58 | MS_Device_USBTask(&Disk_MS_Interface); 59 | USB_USBTask(); 60 | return !ejected; 61 | } 62 | 63 | /** Event handler for the library USB Configuration Changed event. */ 64 | void EVENT_USB_Device_ConfigurationChanged(void) 65 | { 66 | MS_Device_ConfigureEndpoints(&Disk_MS_Interface); 67 | } 68 | 69 | /** Event handler for the library USB Control Request reception event. */ 70 | void EVENT_USB_Device_ControlRequest(void) 71 | { 72 | MS_Device_ProcessControlRequest(&Disk_MS_Interface); 73 | } 74 | 75 | /** Mass Storage class driver callback function the reception of SCSI commands from the host, which must be processed. 76 | * 77 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface configuration structure being referenced 78 | */ 79 | bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 80 | { 81 | return SCSI_DecodeSCSICommand(MSInterfaceInfo); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /mass-storage/mass_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef MASS_STORAGE_H 2 | #define MASS_STORAGE_H 3 | 4 | #include 5 | 6 | void mass_storage_init(void); 7 | void mass_storage_eject(void); 8 | bool usb_task(void); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /mass-storage/scsi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Based on code from the LUFA Library, with the following 3 | copyright information: 4 | 5 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 6 | 7 | Permission to use, copy, modify, distribute, and sell this 8 | software and its documentation for any purpose is hereby granted 9 | without fee, provided that the above copyright notice appear in 10 | all copies and that both that the copyright notice and this 11 | permission notice and warranty disclaimer appear in supporting 12 | documentation, and that the name of the author not be used in 13 | advertising or publicity pertaining to distribution of the 14 | software without specific, written prior permission. 15 | 16 | The author disclaims all warranties with regard to this 17 | software, including all implied warranties of merchantability 18 | and fitness. In no event shall the author be liable for any 19 | special, indirect or consequential damages or any damages 20 | whatsoever resulting from loss of use, data or profits, whether 21 | in an action of contract, negligence or other tortious action, 22 | arising out of or in connection with the use or performance of 23 | this software. 24 | */ 25 | 26 | /** \file 27 | * 28 | * SCSI command processing routines, for SCSI commands issued by the host. Mass Storage 29 | * devices use a thin "Bulk-Only Transport" protocol for issuing commands and status information, 30 | * which wrap around standard SCSI device commands for controlling the actual storage medium. 31 | */ 32 | 33 | #include "mass_storage.h" 34 | #include "scsi.h" 35 | #include "sd-reader/sd_manager.h" 36 | #include "sd-reader/sd_raw.h" 37 | 38 | /* Forward declarations */ 39 | static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 40 | static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 41 | static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 42 | static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 43 | static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 44 | const bool IsDataRead); 45 | static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 46 | 47 | 48 | /** Structure to hold the SCSI response data to a SCSI INQUIRY command. This gives information about the device's 49 | * features and capabilities. 50 | */ 51 | static const SCSI_Inquiry_Response_t InquiryData = 52 | { 53 | .DeviceType = DEVICE_TYPE_BLOCK, 54 | .PeripheralQualifier = 0, 55 | 56 | .Removable = true, 57 | 58 | .Version = 0, 59 | 60 | .ResponseDataFormat = 2, 61 | .NormACA = false, 62 | .TrmTsk = false, 63 | .AERC = false, 64 | 65 | .AdditionalLength = 0x1F, 66 | 67 | .SoftReset = false, 68 | .CmdQue = false, 69 | .Linked = false, 70 | .Sync = false, 71 | .WideBus16Bit = false, 72 | .WideBus32Bit = false, 73 | .RelAddr = false, 74 | 75 | .VendorID = "MJK", 76 | .ProductID = "Bumpy", 77 | .RevisionID = {'0','.','0','0'}, 78 | }; 79 | 80 | /** Structure to hold the sense data for the last issued SCSI command, which is returned to the host after a SCSI REQUEST SENSE 81 | * command is issued. This gives information on exactly why the last command failed to complete. 82 | */ 83 | static SCSI_Request_Sense_Response_t SenseData = 84 | { 85 | .ResponseCode = 0x70, 86 | .AdditionalLength = 0x0A, 87 | }; 88 | 89 | /** Local variable to hold total number of blocks. 90 | */ 91 | static uint32_t LUN_MEDIA_BLOCKS = 0; 92 | 93 | 94 | /** Main routine to process the SCSI command located in the Command Block Wrapper read from the host. This dispatches 95 | * to the appropriate SCSI command handling routine if the issued command is supported by the device, else it returns 96 | * a command failure due to a ILLEGAL REQUEST. 97 | * 98 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 99 | * 100 | * \return Boolean \c true if the command completed successfully, \c false otherwise 101 | */ 102 | bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 103 | { 104 | bool CommandSuccess = false; 105 | 106 | /* Run the appropriate SCSI command hander function based on the passed command */ 107 | switch (MSInterfaceInfo->State.CommandBlock.SCSICommandData[0]) 108 | { 109 | case SCSI_CMD_INQUIRY: 110 | CommandSuccess = SCSI_Command_Inquiry(MSInterfaceInfo); 111 | break; 112 | case SCSI_CMD_REQUEST_SENSE: 113 | CommandSuccess = SCSI_Command_Request_Sense(MSInterfaceInfo); 114 | break; 115 | case SCSI_CMD_READ_CAPACITY_10: 116 | CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo); 117 | break; 118 | case SCSI_CMD_SEND_DIAGNOSTIC: 119 | CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo); 120 | break; 121 | case SCSI_CMD_WRITE_10: 122 | CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE); 123 | break; 124 | case SCSI_CMD_READ_10: 125 | CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_READ); 126 | break; 127 | case SCSI_CMD_MODE_SENSE_6: 128 | CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo); 129 | break; 130 | case SCSI_CMD_START_STOP_UNIT: 131 | mass_storage_eject(); 132 | case SCSI_CMD_TEST_UNIT_READY: 133 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 134 | case SCSI_CMD_VERIFY_10: 135 | /* These commands should just succeed, no handling required */ 136 | CommandSuccess = true; 137 | MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; 138 | break; 139 | default: 140 | /* Update the SENSE key to reflect the invalid command */ 141 | SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, 142 | SCSI_ASENSE_INVALID_COMMAND, 143 | SCSI_ASENSEQ_NO_QUALIFIER); 144 | break; 145 | } 146 | 147 | /* Check if command was successfully processed */ 148 | if (CommandSuccess) 149 | { 150 | SCSI_SET_SENSE(SCSI_SENSE_KEY_GOOD, 151 | SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, 152 | SCSI_ASENSEQ_NO_QUALIFIER); 153 | 154 | return true; 155 | } 156 | 157 | return false; 158 | } 159 | 160 | /** Command processing for an issued SCSI INQUIRY command. This command returns information about the device's features 161 | * and capabilities to the host. 162 | * 163 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 164 | * 165 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 166 | */ 167 | static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 168 | { 169 | uint16_t AllocationLength = SwapEndian_16(*(uint16_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[3]); 170 | uint16_t BytesTransferred = MIN(AllocationLength, sizeof(InquiryData)); 171 | 172 | /* Only the standard INQUIRY data is supported, check if any optional INQUIRY bits set */ 173 | if ((MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & ((1 << 0) | (1 << 1))) || 174 | MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]) 175 | { 176 | /* Optional but unsupported bits set - update the SENSE key and fail the request */ 177 | SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, 178 | SCSI_ASENSE_INVALID_FIELD_IN_CDB, 179 | SCSI_ASENSEQ_NO_QUALIFIER); 180 | 181 | return false; 182 | } 183 | 184 | Endpoint_Write_Stream_LE(&InquiryData, BytesTransferred, NULL); 185 | 186 | /* Pad out remaining bytes with 0x00 */ 187 | Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL); 188 | 189 | /* Finalize the stream transfer to send the last packet */ 190 | Endpoint_ClearIN(); 191 | 192 | /* Succeed the command and update the bytes transferred counter */ 193 | MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; 194 | 195 | return true; 196 | } 197 | 198 | /** Command processing for an issued SCSI REQUEST SENSE command. This command returns information about the last issued command, 199 | * including the error code and additional error information so that the host can determine why a command failed to complete. 200 | * 201 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 202 | * 203 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 204 | */ 205 | static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 206 | { 207 | uint8_t AllocationLength = MSInterfaceInfo->State.CommandBlock.SCSICommandData[4]; 208 | uint8_t BytesTransferred = MIN(AllocationLength, sizeof(SenseData)); 209 | 210 | Endpoint_Write_Stream_LE(&SenseData, BytesTransferred, NULL); 211 | Endpoint_Null_Stream((AllocationLength - BytesTransferred), NULL); 212 | Endpoint_ClearIN(); 213 | 214 | /* Succeed the command and update the bytes transferred counter */ 215 | MSInterfaceInfo->State.CommandBlock.DataTransferLength -= BytesTransferred; 216 | 217 | return true; 218 | } 219 | 220 | /** Command processing for an issued SCSI READ CAPACITY (10) command. This command returns information about the device's capacity 221 | * on the selected Logical Unit (drive), as a number of OS-sized blocks. 222 | * 223 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 224 | * 225 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 226 | */ 227 | static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 228 | { 229 | LUN_MEDIA_BLOCKS = sd_get_blocks(); 230 | uint32_t LastBlockAddressInLUN = LUN_MEDIA_BLOCKS - 1; 231 | uint32_t MediaBlockSize = VIRTUAL_MEMORY_BLOCK_SIZE; 232 | 233 | Endpoint_Write_Stream_BE(&LastBlockAddressInLUN, sizeof(LastBlockAddressInLUN), NULL); 234 | Endpoint_Write_Stream_BE(&MediaBlockSize, sizeof(MediaBlockSize), NULL); 235 | Endpoint_ClearIN(); 236 | 237 | /* Succeed the command and update the bytes transferred counter */ 238 | MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 8; 239 | 240 | return true; 241 | } 242 | 243 | /** Command processing for an issued SCSI SEND DIAGNOSTIC command. This command performs a quick check of the Dataflash ICs on the 244 | * board, and indicates if they are present and functioning correctly. Only the Self-Test portion of the diagnostic command is 245 | * supported. 246 | * 247 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 248 | * 249 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 250 | */ 251 | static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 252 | { 253 | /* Check to see if the SELF TEST bit is not set */ 254 | if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2))) 255 | { 256 | /* Only self-test supported - update SENSE key and fail the command */ 257 | SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, 258 | SCSI_ASENSE_INVALID_FIELD_IN_CDB, 259 | SCSI_ASENSEQ_NO_QUALIFIER); 260 | 261 | return false; 262 | } 263 | 264 | /* Check to see if the attached SD card is functional */ 265 | if (!sd_raw_available()) 266 | { 267 | /* Update SENSE key with a hardware error condition and return command fail */ 268 | SCSI_SET_SENSE(SCSI_SENSE_KEY_HARDWARE_ERROR, 269 | SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, 270 | SCSI_ASENSEQ_NO_QUALIFIER); 271 | 272 | return false; 273 | } 274 | 275 | /* Succeed the command and update the bytes transferred counter */ 276 | MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0; 277 | 278 | return true; 279 | } 280 | 281 | /** Command processing for an issued SCSI READ (10) or WRITE (10) command. This command reads in the block start address 282 | * and total number of blocks to process, then calls the appropriate low-level Dataflash routine to handle the actual 283 | * reading and writing of the data. 284 | * 285 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 286 | * \param[in] IsDataRead Indicates if the command is a READ (10) command or WRITE (10) command (DATA_READ or DATA_WRITE) 287 | * 288 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 289 | */ 290 | static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 291 | const bool IsDataRead) 292 | { 293 | uint32_t BlockAddress; 294 | uint16_t TotalBlocks; 295 | 296 | /* Check if the disk is write protected or not */ 297 | if ((IsDataRead == DATA_WRITE) && DISK_READ_ONLY) 298 | { 299 | /* Block address is invalid, update SENSE key and return command fail */ 300 | SCSI_SET_SENSE(SCSI_SENSE_KEY_DATA_PROTECT, 301 | SCSI_ASENSE_WRITE_PROTECTED, 302 | SCSI_ASENSEQ_NO_QUALIFIER); 303 | 304 | return false; 305 | } 306 | 307 | /* Load in the 32-bit block address (SCSI uses big-endian, so have to reverse the byte order) */ 308 | BlockAddress = SwapEndian_32(*(uint32_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[2]); 309 | 310 | /* Load in the 16-bit total blocks (SCSI uses big-endian, so have to reverse the byte order) */ 311 | TotalBlocks = SwapEndian_16(*(uint16_t*)&MSInterfaceInfo->State.CommandBlock.SCSICommandData[7]); 312 | 313 | /* Check if the block address is outside the maximum allowable value for the LUN */ 314 | if (BlockAddress >= LUN_MEDIA_BLOCKS) 315 | { 316 | /* Block address is invalid, update SENSE key and return command fail */ 317 | SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST, 318 | SCSI_ASENSE_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, 319 | SCSI_ASENSEQ_NO_QUALIFIER); 320 | 321 | return false; 322 | } 323 | 324 | /* Determine if the packet is a READ (10) or WRITE (10) command, call appropriate function */ 325 | if (IsDataRead == DATA_READ) { 326 | /* printf("Reading 0x%x blocks from 0x%lx\n", TotalBlocks, BlockAddress); */ 327 | sd_read_blocks(MSInterfaceInfo, BlockAddress, TotalBlocks); 328 | } else { 329 | /* printf("Writing 0x%x blocks from 0x%lx\n", TotalBlocks, BlockAddress); */ 330 | sd_write_blocks(MSInterfaceInfo, BlockAddress, TotalBlocks); 331 | } 332 | 333 | /* Update the bytes transferred counter and succeed the command */ 334 | MSInterfaceInfo->State.CommandBlock.DataTransferLength -= ((uint32_t)TotalBlocks) << VIRTUAL_MEMORY_BLOCK_SHIFT; 335 | 336 | return true; 337 | } 338 | 339 | /** Command processing for an issued SCSI MODE SENSE (6) command. This command returns various informational pages about 340 | * the SCSI device, as well as the device's Write Protect status. 341 | * 342 | * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with 343 | * 344 | * \return Boolean \c true if the command completed successfully, \c false otherwise. 345 | */ 346 | static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) 347 | { 348 | /* Send an empty header response with the Write Protect flag status */ 349 | Endpoint_Write_8(0x00); 350 | Endpoint_Write_8(0x00); 351 | Endpoint_Write_8(DISK_READ_ONLY ? 0x80 : 0x00); 352 | Endpoint_Write_8(0x00); 353 | Endpoint_ClearIN(); 354 | 355 | /* Update the bytes transferred counter and succeed the command */ 356 | MSInterfaceInfo->State.CommandBlock.DataTransferLength -= 4; 357 | 358 | return true; 359 | } 360 | 361 | 362 | -------------------------------------------------------------------------------- /mass-storage/scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Based on code from the LUFA Library, with the following 3 | copyright information: 4 | 5 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 6 | 7 | Permission to use, copy, modify, distribute, and sell this 8 | software and its documentation for any purpose is hereby granted 9 | without fee, provided that the above copyright notice appear in 10 | all copies and that both that the copyright notice and this 11 | permission notice and warranty disclaimer appear in supporting 12 | documentation, and that the name of the author not be used in 13 | advertising or publicity pertaining to distribution of the 14 | software without specific, written prior permission. 15 | 16 | The author disclaims all warranties with regard to this 17 | software, including all implied warranties of merchantability 18 | and fitness. In no event shall the author be liable for any 19 | special, indirect or consequential damages or any damages 20 | whatsoever resulting from loss of use, data or profits, whether 21 | in an action of contract, negligence or other tortious action, 22 | arising out of or in connection with the use or performance of 23 | this software. 24 | */ 25 | 26 | /** \file 27 | * 28 | * Header file for SCSI.c. 29 | */ 30 | 31 | #ifndef _SCSI_H_ 32 | #define _SCSI_H_ 33 | 34 | /* Includes: */ 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | /*****************************************************************************/ 41 | 42 | /* Configuration */ 43 | #define TOTAL_LUNS 1 44 | 45 | /** Block size of the device. This is kept at 512 to remain compatible with the OS despite the underlying 46 | * storage media (Dataflash) using a different native block size. Do not change this value. 47 | */ 48 | #define VIRTUAL_MEMORY_BLOCK_SHIFT 9 49 | #define VIRTUAL_MEMORY_BLOCK_SIZE (1 << VIRTUAL_MEMORY_BLOCK_SHIFT) 50 | 51 | #define DISK_READ_ONLY false 52 | 53 | /*****************************************************************************/ 54 | 55 | /* Macros: */ 56 | /** Macro to set the current SCSI sense data to the given key, additional sense code and additional sense qualifier. This 57 | * is for convenience, as it allows for all three sense values (returned upon request to the host to give information about 58 | * the last command failure) in a quick and easy manner. 59 | * 60 | * \param[in] Key New SCSI sense key to set the sense code to 61 | * \param[in] Acode New SCSI additional sense key to set the additional sense code to 62 | * \param[in] Aqual New SCSI additional sense key qualifier to set the additional sense qualifier code to 63 | */ 64 | #define SCSI_SET_SENSE(Key, Acode, Aqual) MACROS{ SenseData.SenseKey = (Key); \ 65 | SenseData.AdditionalSenseCode = (Acode); \ 66 | SenseData.AdditionalSenseQualifier = (Aqual); }MACROE 67 | 68 | /** Macro for the \ref SCSI_Command_ReadWrite_10() function, to indicate that data is to be read from the storage medium. */ 69 | #define DATA_READ true 70 | 71 | /** Macro for the \ref SCSI_Command_ReadWrite_10() function, to indicate that data is to be written to the storage medium. */ 72 | #define DATA_WRITE false 73 | 74 | /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a Block Media device. */ 75 | #define DEVICE_TYPE_BLOCK 0x00 76 | 77 | /** Value for the DeviceType entry in the SCSI_Inquiry_Response_t enum, indicating a CD-ROM device. */ 78 | #define DEVICE_TYPE_CDROM 0x05 79 | 80 | /* Function Prototypes: */ 81 | bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo); 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /mp3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "mp3.h" 6 | 7 | int mp3_volume = 5; 8 | 9 | void mp3_write(const uint8_t addr, const uint16_t data); 10 | static void mp3_write_volume(void); 11 | 12 | 13 | // Initializes and checks mp3 player. Returns 1 if successful, 0 otherwise. 14 | int mp3_init(void) 15 | { 16 | // MP3 chip select, data select, chip reset 17 | DDRF |= (1 << PF4) | (1 << PF1) | (1 << PF5); 18 | PORTF |= (1 << PF4) | (1 << PF1) | (1 << PF5); 19 | 20 | // All of the spi stuff is configured by the SD card, 21 | // so we don't need to do anything here. 22 | 23 | // Turn SPI frequency doubling off for this one communication 24 | SPSR &= ~(1 << SPI2X); 25 | 26 | // Turn up clock multiplier 27 | mp3_write(0x3, 0x9800); 28 | 29 | // Turn SPI frequency doubling back on, since the VS1003 now 30 | // has a clock multiplier enabled and can talk fast. 31 | SPSR |= (1 << SPI2X); 32 | 33 | // Check to make sure that this chip is the right one. 34 | const uint16_t version = (mp3_read(0x1) & 0xf0) >> 4; 35 | if (version != 3) 36 | return 0; 37 | 38 | mp3_write_volume(); 39 | 40 | return 1; 41 | } 42 | 43 | static inline void mp3_select(void) 44 | { 45 | PORTF &= ~(1 << PF4); 46 | } 47 | 48 | static inline void mp3_deselect(void) 49 | { 50 | PORTF |= (1 << PF4); 51 | } 52 | 53 | static inline void mp3_data_select(void) 54 | { 55 | PORTF &= ~(1 << PF1); 56 | } 57 | 58 | static inline void mp3_data_deselect(void) 59 | { 60 | PORTF |= (1 << PF1); 61 | } 62 | 63 | // Checks DREQ line and returns True if it's high 64 | inline bool mp3_wants_data(void) 65 | { 66 | return PINF & (1 << PF6); 67 | } 68 | 69 | 70 | void mp3_send_data(uint8_t* buffer) 71 | { 72 | // Select the mp3 for a data transmission 73 | mp3_data_select(); 74 | 75 | for (int i=0; i < MP3_BUFFER_SIZE; ++i) 76 | { 77 | SPI_SendByte(buffer[i]); 78 | } 79 | 80 | // Deselect SDI port. 81 | mp3_data_deselect(); 82 | } 83 | 84 | static inline void mp3_wait(void) 85 | { 86 | // Wait for DREQ to go high (signaling that the mp3 chip 87 | // can take in an SPI command). 88 | while(!(PINF & (1 << PF6))); 89 | } 90 | 91 | 92 | uint16_t mp3_read(const uint8_t addr) 93 | { 94 | mp3_wait(); 95 | 96 | // Select the mp3 for a data transmission 97 | mp3_select(); 98 | 99 | SPI_SendByte(0b00000011); // opcode for read 100 | SPI_SendByte(addr); 101 | 102 | // Use dummy sends to get out data 103 | uint16_t out = 0; 104 | out = SPI_ReceiveByte(); 105 | out <<= 8; 106 | out = SPI_ReceiveByte(); 107 | 108 | mp3_deselect(); 109 | 110 | return out; 111 | } 112 | 113 | void mp3_write(const uint8_t addr, const uint16_t data) 114 | { 115 | mp3_wait(); 116 | mp3_select(); 117 | 118 | SPI_SendByte(0b00000010); // opcode for write 119 | SPI_SendByte(addr); 120 | SPI_SendByte(data >> 8); 121 | SPI_SendByte(data & 0xff); 122 | 123 | mp3_deselect(); 124 | } 125 | 126 | 127 | static void mp3_write_volume(void) 128 | { 129 | uint16_t v = 10*(8 - mp3_volume); 130 | mp3_write(0xb, (v << 8) | v); 131 | } 132 | 133 | void mp3_volume_up(void) 134 | { 135 | if (mp3_volume < 8) 136 | { 137 | mp3_volume++; 138 | mp3_write_volume(); 139 | } 140 | } 141 | 142 | 143 | void mp3_volume_down(void) 144 | { 145 | if (mp3_volume > 1) 146 | { 147 | mp3_volume--; 148 | mp3_write_volume(); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /mp3.h: -------------------------------------------------------------------------------- 1 | #ifndef MP3_H 2 | #define MP3_H 3 | 4 | #include 5 | #include 6 | 7 | #define MP3_BUFFER_SIZE 32 8 | 9 | extern int mp3_volume; 10 | 11 | int mp3_init(void); 12 | 13 | uint16_t mp3_read(const uint8_t addr); 14 | bool mp3_wants_data(void); 15 | void mp3_send_data(uint8_t* buffer); 16 | 17 | void mp3_volume_up(void); 18 | void mp3_volume_down(void); 19 | #endif 20 | -------------------------------------------------------------------------------- /player.c: -------------------------------------------------------------------------------- 1 | #include "player.h" 2 | #include "sd.h" 3 | #include "encoder.h" 4 | #include "LEDs.h" 5 | #include "mp3.h" 6 | #include "tenths.h" 7 | 8 | struct Buffer buffer; 9 | struct PlayerState player_state; 10 | 11 | void player_manage_buffer(void) 12 | { 13 | if (buffer.empty) 14 | { 15 | sd_mount_filesystem(); 16 | 17 | // Copy data from the SD card into our local the buffer. 18 | // If we're out of data, skip to the next song. 19 | while (!sd_get_data(buffer.data, MP3_BUFFER_SIZE)) 20 | { 21 | sd_next_song(); 22 | } 23 | } 24 | buffer.empty = false; 25 | 26 | // Move data from our buffer to the mp3 decoder's buffer. 27 | if (mp3_wants_data() && player_state.playing) 28 | { 29 | mp3_send_data(buffer.data); 30 | buffer.empty = true; 31 | } 32 | } 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | // Redraws LEDs based on player state 37 | void player_redraw(void) 38 | { 39 | if (player_state.sleeping) 40 | LEDs_sleep(); 41 | else 42 | LEDs_volume(player_state.playing, mp3_volume); 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | // Check if we should wake from sleep mode by detecting double-tap 48 | static void player_check_wake(void) 49 | { 50 | // Look for a double-tap to detect wake-up. 51 | if (player_state.sleeping && tenths - player_state.button_time < 5) { 52 | player_state.sleeping = false; 53 | player_state.scrolled = true; // dummy scroll to prevent play-pause 54 | player_redraw(); 55 | } 56 | } 57 | 58 | // On button press, check if we should wake up from sleep mode. 59 | static void player_button_pressed(void) 60 | { 61 | // Reset the variable that tracks whether we've scrolled 62 | player_state.scrolled = false; 63 | 64 | // Look for a double-tap within 0.5 seconds 65 | // to wake up from sleep mode. 66 | player_check_wake(); 67 | 68 | // Keep track of when this button was pressed 69 | // (because we go into sleep mode after 5 seconds) 70 | player_state.button_time = tenths; 71 | } 72 | 73 | 74 | // On button release, switch play/pause unless we've scrolled 75 | static void player_button_released(void) 76 | { 77 | // If we've scrolled, then button release doesn't matter. 78 | // Otherwise, a single tap toggles play/pause 79 | if (!player_state.sleeping && !player_state.scrolled) 80 | { 81 | player_state.playing = !player_state.playing; 82 | player_redraw(); 83 | } 84 | } 85 | 86 | 87 | // Check to see if we should enter sleep mode. 88 | static void player_check_sleep(void) 89 | { 90 | if (player_state.button_down && !player_state.sleeping && 91 | !player_state.scrolled && tenths - player_state.button_time >= 50) 92 | { 93 | player_state.sleeping = true; 94 | player_redraw(); 95 | } 96 | } 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | 100 | static void player_animate_next(void) 101 | { 102 | LEDs_next(); 103 | sd_next_song(); 104 | player_redraw(); 105 | } 106 | 107 | static void player_animate_prev(void) 108 | { 109 | LEDs_prev(); 110 | sd_prev_song(); 111 | player_redraw(); 112 | } 113 | 114 | static void player_volume_up(void) 115 | { 116 | mp3_volume_up(); 117 | player_redraw(); 118 | } 119 | 120 | static void player_volume_down(void) 121 | { 122 | mp3_volume_down(); 123 | player_redraw(); 124 | } 125 | //////////////////////////////////////////////////////////////////////////////// 126 | 127 | void player_update_state(void) 128 | { 129 | bool button_down = encoder_switch; 130 | 131 | if (button_down && !player_state.button_down) 132 | player_button_pressed(); 133 | else if (player_state.button_down && !button_down) 134 | player_button_released(); 135 | 136 | player_state.button_down = button_down; 137 | 138 | int scroll = encoder; 139 | encoder_clear(); 140 | 141 | if (scroll && !player_state.sleeping) 142 | { 143 | player_state.scrolled = true; 144 | if (scroll > 0) { 145 | if (button_down) player_animate_next(); 146 | else player_volume_up(); 147 | } else { 148 | if (button_down) player_animate_prev(); 149 | else player_volume_down(); 150 | } 151 | } 152 | 153 | // Go to sleep if necessary 154 | player_check_sleep(); 155 | } 156 | 157 | //////////////////////////////////////////////////////////////////////////////// 158 | 159 | void player_init(void) 160 | { 161 | buffer.empty = true; 162 | player_state.playing = false; 163 | player_state.sleeping = false; 164 | player_state.button_down = false; 165 | player_redraw(); 166 | } 167 | -------------------------------------------------------------------------------- /player.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H 2 | #define PLAYER_H 3 | 4 | #include "mp3.h" 5 | 6 | struct Buffer { 7 | uint8_t data[MP3_BUFFER_SIZE]; 8 | bool empty; 9 | }; 10 | 11 | extern struct Buffer buffer; 12 | 13 | struct PlayerState { 14 | bool playing; 15 | bool sleeping; 16 | bool button_down; 17 | bool scrolled; 18 | unsigned button_time; 19 | }; 20 | 21 | extern struct PlayerState player_state; 22 | 23 | void player_manage_buffer(void); 24 | void player_update_state(void); 25 | void player_init(void); 26 | void player_redraw(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This repository contains firmware for the [bumpy](http://mattkeeter.com/projects/bumpy) 4 | mp3 player. 5 | 6 | ## License 7 | Unless otherwise stated (e.g. in third-party libraries), all code is licensed as follows: 8 | 9 | Copyright (c) 2014 Matthew Keeter 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /sd-reader/FAQ: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions for sd-reader 2 | ======================================== 3 | 4 | General 5 | ------- 6 | 7 | Q: Which cards are supported? 8 | A: All MMC/SD/SDHC/miniSD/microSD/microSDHC should work, although not all variants have been tested. 9 | Some very old (low capacity) cards might be broken as well. Cards with a capacity of 16 MB or 10 | less are usually formatted with FAT12, so these are supported in raw mode only, if at all. 11 | 12 | Q: What data rates can I expect? 13 | A: See the benchmark page on the homepage. 14 | http://www.roland-riegel.de/sd-reader/benchmarks/ 15 | 16 | Q: Are there boards available for purchase? 17 | A: No. 18 | 19 | Hardware 20 | -------- 21 | 22 | Q: Where can I find the schematic? 23 | A: Get it on the homepage. 24 | http://www.roland-riegel.de/sd-reader/sd-reader_circuit_latest.zip 25 | 26 | Q: What if my card socket has no Card-Detect and/or Card-Lock switches? 27 | A: Change sd_raw_config.h such that it looks like 28 | 29 | #define configure_pin_available() /* nothing */ 30 | #define configure_pin_locked() /* nothing */ 31 | 32 | #define get_pin_available() 0 33 | #define get_pin_locked() 1 34 | 35 | Q: All attempts to write to the card fail, although reading works. What's the problem? 36 | A: Enable write support within sd_raw_config.h. And probably, your card socket has no Card-lock 37 | detection (see question above). 38 | 39 | Q: The card initialization fails. What can I do? 40 | A: Usually this is some kind of hardware problem. 41 | * Check the physical connections. 42 | * Keep the signal lines to the card as short as possible. 43 | * Do not use diodes to derive the card's supply voltage. Use a 3.3V voltage regulator instead. 44 | * Have a stable, buffered power supply and use capacitors for correct voltage regulator 45 | operation (see the schematics linked above). 46 | * Use extra capacitors of 50uF and 100nF as close to the card as possible. 47 | * When using an integrated level shifter or no level shifting at all (see the next question), 48 | try adding a pullup of 50k from the data-out line of the card to 3.3V. 49 | * Make sure the limiting frequency of the level shifter is not exceeded. Have a look into its 50 | datasheet! 51 | * Check the signals with a scope. 52 | 53 | Q: What alternatives to resistor level shifting exist? 54 | A: If you want to use additional devices with SPI or the resistor solution appears too ugly, there 55 | are two possibilities. Either operate the whole circuit with a single 3.3V supply and no level 56 | shifting at all or use a level shifting IC which interfaces the memory card to the AVR. 57 | Depending on your requirements, adequate devices could include MAX3378E, MAX3392E, MAX3395E, 58 | 74LVC245 and 74HCT4050 (optionally together with 74HCT125). Please check the datasheets for the 59 | required DC/AC characteristics! 60 | 61 | Software 62 | -------- 63 | 64 | Q: What's the software license? 65 | A: GPLv2 or (for most parts) LGPLv2.1. Before using a file, read its license terms included at the 66 | beginning of the file. 67 | 68 | Q: What's the programming language used? 69 | A: It's C, in compliance with the ISO C99 standard. 70 | 71 | Q: What are these .patch-files provided? 72 | A: Those record the source code differences between the old and new release. If you edited your 73 | private sd-reader version, it might be easier to apply the patch files using the "patch" utility 74 | common on Unix-like systems, rather than manually inserting the changes by hand. For Windows 75 | users, the GnuWin32 project provides a port of "patch". 76 | 77 | Q: Where can I learn more about the library interface and how to use it? 78 | A: Look into the HTML documentation provided online or within each source distribution. Also take 79 | the provided main.c as an example application and as a starting point. 80 | 81 | Q: How do I adapt it to an ATmegaXYZ and my circuit in particular? 82 | A: Add your MCU-specific pin configuration to sd_raw_config.h. Some commonly used ones are already 83 | included. 84 | 85 | Q: How do I adapt it to a different MCU clock? 86 | A: Change the MCU_FREQ variable within the Makefile. 87 | 88 | Q: How do I adapt it to some different MCU architecture? 89 | A: Change sd_raw_init(), sd_raw_send_byte(), sd_raw_rec_byte() within sd_raw.c and the pin 90 | definitions within sd_raw_config.h appropriately. 91 | 92 | Q: Can the library be used with Arduino? 93 | A: Yes. I do not have any experience with Arduino myself, but people report that it works with some 94 | minor modifications to the library code needed due to some different compiler settings. Please 95 | search the web for details. 96 | 97 | Q: Can I use software SPI? 98 | A: Yes, but the code is not prepared for it. 99 | 100 | Q: My application crashes somewhere in the lib. Is there some bug hidden? 101 | A: There might be a bug. Much more likely, however, is that you experience memory problems, 102 | especially heap/stack collisions. The crashes can appear everywhere, but typically this is not 103 | the place where the real problem is. Try to minimize the size of structures and other memory 104 | blocks your application uses. Sum up the amount of memory your application allocates with global 105 | and local variables and the memory you allocate dynamically with malloc(). The avr-nm utility 106 | also helps a lot here. When called with the "--print-size --size-sort" parameters, it lists all 107 | symbols and their code/memory size within the given file. See the avr-libc FAQ and the nm manual 108 | for further information. 109 | http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ramoverlap 110 | http://sourceware.org/binutils/docs/binutils/nm.html 111 | 112 | Q: Opening the FAT filesystem fails. What can I do? 113 | A: Make sure there is a FAT16 or FAT32 filesystem on the card. This usually isn't possible for old 114 | cards with 16 MB or less. For larger ones, format the cards using the OS utilities, like the 115 | Windows Explorer, the Unix/Linux mkdosfs command or special SD card format tools. 116 | http://panasonic.jp/support/global/cs/sd/download/sd_formatter.html 117 | http://www.sdcard.org/consumers/formatter/ 118 | 119 | Q: Writing to the card returns no failure, but when checking the file's content the data is not 120 | there. What happens? 121 | A: For performance reasons, writing to the card is always buffered. Before pulling the card out of 122 | its socket (or issuing a reset of the MCU), make sure sd_raw_sync() gets called such that all 123 | buffered data is written out to permanent card storage. 124 | 125 | -------------------------------------------------------------------------------- /sd-reader/byteordering.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include "byteordering.h" 12 | 13 | /** 14 | * \addtogroup byteordering 15 | * 16 | * Architecture-dependent handling of byte-ordering. 17 | * 18 | * @{ 19 | */ 20 | /** 21 | * \file 22 | * Byte-order handling implementation (license: GPLv2 or LGPLv2.1) 23 | * 24 | * \author Roland Riegel 25 | */ 26 | 27 | #if DOXYGEN || SWAP_NEEDED 28 | 29 | /** 30 | * \internal 31 | * Swaps the bytes of a 16-bit integer. 32 | * 33 | * \param[in] i A 16-bit integer which to swap. 34 | * \returns The swapped 16-bit integer. 35 | */ 36 | uint16_t swap16(uint16_t i) 37 | { 38 | return SWAP16(i); 39 | } 40 | 41 | /** 42 | * \internal 43 | * Swaps the bytes of a 32-bit integer. 44 | * 45 | * \param[in] i A 32-bit integer which to swap. 46 | * \returns The swapped 32-bit integer. 47 | */ 48 | uint32_t swap32(uint32_t i) 49 | { 50 | return SWAP32(i); 51 | } 52 | 53 | #endif 54 | 55 | /** 56 | * Reads a 16-bit integer from memory in little-endian byte order. 57 | * 58 | * \param[in] p Pointer from where to read the integer. 59 | * \returns The 16-bit integer read from memory. 60 | */ 61 | uint16_t read16(const uint8_t* p) 62 | { 63 | return (((uint16_t) p[1]) << 8) | 64 | (((uint16_t) p[0]) << 0); 65 | } 66 | 67 | /** 68 | * Reads a 32-bit integer from memory in little-endian byte order. 69 | * 70 | * \param[in] p Pointer from where to read the integer. 71 | * \returns The 32-bit integer read from memory. 72 | */ 73 | uint32_t read32(const uint8_t* p) 74 | { 75 | return (((uint32_t) p[3]) << 24) | 76 | (((uint32_t) p[2]) << 16) | 77 | (((uint32_t) p[1]) << 8) | 78 | (((uint32_t) p[0]) << 0); 79 | } 80 | 81 | /** 82 | * Writes a 16-bit integer into memory in little-endian byte order. 83 | * 84 | * \param[in] p Pointer where to write the integer to. 85 | * \param[in] i The 16-bit integer to write. 86 | */ 87 | void write16(uint8_t* p, uint16_t i) 88 | { 89 | p[1] = (uint8_t) ((i & 0xff00) >> 8); 90 | p[0] = (uint8_t) ((i & 0x00ff) >> 0); 91 | } 92 | 93 | /** 94 | * Writes a 32-bit integer into memory in little-endian byte order. 95 | * 96 | * \param[in] p Pointer where to write the integer to. 97 | * \param[in] i The 32-bit integer to write. 98 | */ 99 | void write32(uint8_t* p, uint32_t i) 100 | { 101 | p[3] = (uint8_t) ((i & 0xff000000) >> 24); 102 | p[2] = (uint8_t) ((i & 0x00ff0000) >> 16); 103 | p[1] = (uint8_t) ((i & 0x0000ff00) >> 8); 104 | p[0] = (uint8_t) ((i & 0x000000ff) >> 0); 105 | } 106 | 107 | /** 108 | * @} 109 | */ 110 | 111 | -------------------------------------------------------------------------------- /sd-reader/byteordering.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef BYTEORDERING_H 12 | #define BYTEORDERING_H 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" 18 | { 19 | #endif 20 | 21 | /** 22 | * \addtogroup byteordering 23 | * 24 | * @{ 25 | */ 26 | /** 27 | * \file 28 | * Byte-order handling header (license: GPLv2 or LGPLv2.1) 29 | * 30 | * \author Roland Riegel 31 | */ 32 | 33 | #define SWAP16(val) ((((uint16_t) (val)) << 8) | \ 34 | (((uint16_t) (val)) >> 8) \ 35 | ) 36 | #define SWAP32(val) (((((uint32_t) (val)) & 0x000000ff) << 24) | \ 37 | ((((uint32_t) (val)) & 0x0000ff00) << 8) | \ 38 | ((((uint32_t) (val)) & 0x00ff0000) >> 8) | \ 39 | ((((uint32_t) (val)) & 0xff000000) >> 24) \ 40 | ) 41 | 42 | #if LITTLE_ENDIAN || __AVR__ 43 | #define SWAP_NEEDED 0 44 | #elif BIG_ENDIAN 45 | #define SWAP_NEEDED 1 46 | #else 47 | #error "Endianess undefined! Please define LITTLE_ENDIAN=1 or BIG_ENDIAN=1." 48 | #endif 49 | 50 | /** 51 | * \def HTOL16(val) 52 | * 53 | * Converts a 16-bit integer from host byte order to little-endian byte order. 54 | * 55 | * Use this macro for compile time constants only. For variable values 56 | * use the function htol16() instead. This saves code size. 57 | * 58 | * \param[in] val A 16-bit integer in host byte order. 59 | * \returns The given 16-bit integer converted to little-endian byte order. 60 | */ 61 | /** 62 | * \def HTOL32(val) 63 | * 64 | * Converts a 32-bit integer from host byte order to little-endian byte order. 65 | * 66 | * Use this macro for compile time constants only. For variable values 67 | * use the function htol32() instead. This saves code size. 68 | * 69 | * \param[in] val A 32-bit integer in host byte order. 70 | * \returns The given 32-bit integer converted to little-endian byte order. 71 | */ 72 | /** 73 | * \def LTOH16(val) 74 | * 75 | * Converts a 16-bit integer from little-endian byte order to host byte order. 76 | * 77 | * Use this macro for compile time constants only. For variable values 78 | * use the function ltoh16() instead. This saves code size. 79 | * 80 | * \param[in] val A 16-bit integer in little-endian byte order. 81 | * \returns The given 16-bit integer converted to host byte order. 82 | */ 83 | /** 84 | * \def LTOH32(val) 85 | * 86 | * Converts a 32-bit integer from little-endian byte order to host byte order. 87 | * 88 | * Use this macro for compile time constants only. For variable values 89 | * use the function ltoh32() instead. This saves code size. 90 | * 91 | * \param[in] val A 32-bit integer in little-endian byte order. 92 | * \returns The given 32-bit integer converted to host byte order. 93 | */ 94 | 95 | #if SWAP_NEEDED 96 | #define HTOL16(val) SWAP16(val) 97 | #define HTOL32(val) SWAP32(val) 98 | #define LTOH16(val) SWAP16(val) 99 | #define LTOH32(val) SWAP32(val) 100 | #else 101 | #define HTOL16(val) (val) 102 | #define HTOL32(val) (val) 103 | #define LTOH16(val) (val) 104 | #define LTOH32(val) (val) 105 | #endif 106 | 107 | #if DOXYGEN 108 | 109 | /** 110 | * Converts a 16-bit integer from host byte order to little-endian byte order. 111 | * 112 | * Use this function on variable values instead of the 113 | * macro HTOL16(). This saves code size. 114 | * 115 | * \param[in] h A 16-bit integer in host byte order. 116 | * \returns The given 16-bit integer converted to little-endian byte order. 117 | */ 118 | uint16_t htol16(uint16_t h); 119 | 120 | /** 121 | * Converts a 32-bit integer from host byte order to little-endian byte order. 122 | * 123 | * Use this function on variable values instead of the 124 | * macro HTOL32(). This saves code size. 125 | * 126 | * \param[in] h A 32-bit integer in host byte order. 127 | * \returns The given 32-bit integer converted to little-endian byte order. 128 | */ 129 | uint32_t htol32(uint32_t h); 130 | 131 | /** 132 | * Converts a 16-bit integer from little-endian byte order to host byte order. 133 | * 134 | * Use this function on variable values instead of the 135 | * macro LTOH16(). This saves code size. 136 | * 137 | * \param[in] l A 16-bit integer in little-endian byte order. 138 | * \returns The given 16-bit integer converted to host byte order. 139 | */ 140 | uint16_t ltoh16(uint16_t l); 141 | 142 | /** 143 | * Converts a 32-bit integer from little-endian byte order to host byte order. 144 | * 145 | * Use this function on variable values instead of the 146 | * macro LTOH32(). This saves code size. 147 | * 148 | * \param[in] l A 32-bit integer in little-endian byte order. 149 | * \returns The given 32-bit integer converted to host byte order. 150 | */ 151 | uint32_t ltoh32(uint32_t l); 152 | 153 | #elif SWAP_NEEDED 154 | 155 | #define htol16(h) swap16(h) 156 | #define htol32(h) swap32(h) 157 | #define ltoh16(l) swap16(l) 158 | #define ltoh32(l) swap32(l) 159 | 160 | #else 161 | 162 | #define htol16(h) (h) 163 | #define htol32(h) (h) 164 | #define ltoh16(l) (l) 165 | #define ltoh32(l) (l) 166 | 167 | #endif 168 | 169 | uint16_t read16(const uint8_t* p); 170 | uint32_t read32(const uint8_t* p); 171 | void write16(uint8_t* p, uint16_t i); 172 | void write32(uint8_t* p, uint32_t i); 173 | 174 | /** 175 | * @} 176 | */ 177 | 178 | #if SWAP_NEEDED 179 | uint16_t swap16(uint16_t i); 180 | uint32_t swap32(uint32_t i); 181 | #endif 182 | 183 | #ifdef __cplusplus 184 | } 185 | #endif 186 | 187 | #endif 188 | 189 | -------------------------------------------------------------------------------- /sd-reader/fat.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef FAT_H 12 | #define FAT_H 13 | 14 | #include 15 | #include "fat_config.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" 19 | { 20 | #endif 21 | 22 | /** 23 | * \addtogroup fat 24 | * 25 | * @{ 26 | */ 27 | /** 28 | * \file 29 | * FAT header (license: GPLv2 or LGPLv2.1) 30 | * 31 | * \author Roland Riegel 32 | */ 33 | 34 | /** 35 | * \addtogroup fat_file 36 | * @{ 37 | */ 38 | 39 | /** The file is read-only. */ 40 | #define FAT_ATTRIB_READONLY (1 << 0) 41 | /** The file is hidden. */ 42 | #define FAT_ATTRIB_HIDDEN (1 << 1) 43 | /** The file is a system file. */ 44 | #define FAT_ATTRIB_SYSTEM (1 << 2) 45 | /** The file is empty and has the volume label as its name. */ 46 | #define FAT_ATTRIB_VOLUME (1 << 3) 47 | /** The file is a directory. */ 48 | #define FAT_ATTRIB_DIR (1 << 4) 49 | /** The file has to be archived. */ 50 | #define FAT_ATTRIB_ARCHIVE (1 << 5) 51 | 52 | /** The given offset is relative to the beginning of the file. */ 53 | #define FAT_SEEK_SET 0 54 | /** The given offset is relative to the current read/write position. */ 55 | #define FAT_SEEK_CUR 1 56 | /** The given offset is relative to the end of the file. */ 57 | #define FAT_SEEK_END 2 58 | 59 | /** 60 | * @} 61 | */ 62 | 63 | struct partition_struct; 64 | struct fat_fs_struct; 65 | struct fat_file_struct; 66 | struct fat_dir_struct; 67 | 68 | /** 69 | * \ingroup fat_file 70 | * Describes a directory entry. 71 | */ 72 | struct fat_dir_entry_struct 73 | { 74 | /** The file's name, truncated to 31 characters. */ 75 | char long_name[32]; 76 | /** The file's attributes. Mask of the FAT_ATTRIB_* constants. */ 77 | uint8_t attributes; 78 | #if FAT_DATETIME_SUPPORT 79 | /** Compressed representation of modification time. */ 80 | uint16_t modification_time; 81 | /** Compressed representation of modification date. */ 82 | uint16_t modification_date; 83 | #endif 84 | /** The cluster in which the file's first byte resides. */ 85 | cluster_t cluster; 86 | /** The file's size. */ 87 | uint32_t file_size; 88 | /** The total disk offset of this directory entry. */ 89 | offset_t entry_offset; 90 | }; 91 | 92 | struct fat_fs_struct* fat_open(struct partition_struct* partition); 93 | void fat_close(struct fat_fs_struct* fs); 94 | 95 | struct fat_file_struct* fat_open_file(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry); 96 | void fat_close_file(struct fat_file_struct* fd); 97 | intptr_t fat_read_file(struct fat_file_struct* fd, uint8_t* buffer, uintptr_t buffer_len); 98 | intptr_t fat_write_file(struct fat_file_struct* fd, const uint8_t* buffer, uintptr_t buffer_len); 99 | uint8_t fat_seek_file(struct fat_file_struct* fd, int32_t* offset, uint8_t whence); 100 | uint8_t fat_resize_file(struct fat_file_struct* fd, uint32_t size); 101 | 102 | struct fat_dir_struct* fat_open_dir(struct fat_fs_struct* fs, const struct fat_dir_entry_struct* dir_entry); 103 | void fat_close_dir(struct fat_dir_struct* dd); 104 | uint8_t fat_read_dir(struct fat_dir_struct* dd, struct fat_dir_entry_struct* dir_entry); 105 | uint8_t fat_reset_dir(struct fat_dir_struct* dd); 106 | 107 | uint8_t fat_create_file(struct fat_dir_struct* parent, const char* file, struct fat_dir_entry_struct* dir_entry); 108 | uint8_t fat_delete_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry); 109 | uint8_t fat_move_file(struct fat_fs_struct* fs, struct fat_dir_entry_struct* dir_entry, struct fat_dir_struct* parent_new, const char* file_new); 110 | uint8_t fat_create_dir(struct fat_dir_struct* parent, const char* dir, struct fat_dir_entry_struct* dir_entry); 111 | #define fat_delete_dir fat_delete_file 112 | #define fat_move_dir fat_move_file 113 | 114 | void fat_get_file_modification_date(const struct fat_dir_entry_struct* dir_entry, uint16_t* year, uint8_t* month, uint8_t* day); 115 | void fat_get_file_modification_time(const struct fat_dir_entry_struct* dir_entry, uint8_t* hour, uint8_t* min, uint8_t* sec); 116 | 117 | uint8_t fat_get_dir_entry_of_path(struct fat_fs_struct* fs, const char* path, struct fat_dir_entry_struct* dir_entry); 118 | 119 | offset_t fat_get_fs_size(const struct fat_fs_struct* fs); 120 | offset_t fat_get_fs_free(const struct fat_fs_struct* fs); 121 | 122 | /** 123 | * @} 124 | */ 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif 131 | 132 | -------------------------------------------------------------------------------- /sd-reader/fat_config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef FAT_CONFIG_H 12 | #define FAT_CONFIG_H 13 | 14 | #include 15 | #include "sd_raw_config.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" 19 | { 20 | #endif 21 | 22 | /** 23 | * \addtogroup fat 24 | * 25 | * @{ 26 | */ 27 | /** 28 | * \file 29 | * FAT configuration (license: GPLv2 or LGPLv2.1) 30 | */ 31 | 32 | /** 33 | * \ingroup fat_config 34 | * Controls FAT write support. 35 | * 36 | * Set to 1 to enable FAT write support, set to 0 to disable it. 37 | */ 38 | #define FAT_WRITE_SUPPORT SD_RAW_WRITE_SUPPORT 39 | 40 | /** 41 | * \ingroup fat_config 42 | * Controls FAT long filename (LFN) support. 43 | * 44 | * Set to 1 to enable LFN support, set to 0 to disable it. 45 | */ 46 | #define FAT_LFN_SUPPORT 1 47 | 48 | /** 49 | * \ingroup fat_config 50 | * Controls FAT date and time support. 51 | * 52 | * Set to 1 to enable FAT date and time stamping support. 53 | */ 54 | #define FAT_DATETIME_SUPPORT 0 55 | 56 | /** 57 | * \ingroup fat_config 58 | * Controls FAT32 support. 59 | * 60 | * Set to 1 to enable FAT32 support. 61 | */ 62 | #define FAT_FAT32_SUPPORT SD_RAW_SDHC 63 | 64 | /** 65 | * \ingroup fat_config 66 | * Controls updates of directory entries. 67 | * 68 | * Set to 1 to delay directory entry updates until the file is closed. 69 | * This can boost performance significantly, but may cause data loss 70 | * if the file is not properly closed. 71 | */ 72 | #define FAT_DELAY_DIRENTRY_UPDATE 0 73 | 74 | /** 75 | * \ingroup fat_config 76 | * Determines the function used for retrieving current date and time. 77 | * 78 | * Define this to the function call which shall be used to retrieve 79 | * current date and time. 80 | * 81 | * \note Used only when FAT_DATETIME_SUPPORT is 1. 82 | * 83 | * \param[out] year Pointer to a \c uint16_t which receives the current year. 84 | * \param[out] month Pointer to a \c uint8_t which receives the current month. 85 | * \param[out] day Pointer to a \c uint8_t which receives the current day. 86 | * \param[out] hour Pointer to a \c uint8_t which receives the current hour. 87 | * \param[out] min Pointer to a \c uint8_t which receives the current minute. 88 | * \param[out] sec Pointer to a \c uint8_t which receives the current sec. 89 | */ 90 | #define fat_get_datetime(year, month, day, hour, min, sec) \ 91 | get_datetime(year, month, day, hour, min, sec) 92 | /* forward declaration for the above */ 93 | void get_datetime(uint16_t* year, uint8_t* month, uint8_t* day, uint8_t* hour, uint8_t* min, uint8_t* sec); 94 | 95 | /** 96 | * \ingroup fat_config 97 | * Maximum number of filesystem handles. 98 | */ 99 | #define FAT_FS_COUNT 1 100 | 101 | /** 102 | * \ingroup fat_config 103 | * Maximum number of file handles. 104 | */ 105 | #define FAT_FILE_COUNT 1 106 | 107 | /** 108 | * \ingroup fat_config 109 | * Maximum number of directory handles. 110 | */ 111 | #define FAT_DIR_COUNT 2 112 | 113 | /** 114 | * @} 115 | */ 116 | 117 | #if FAT_FAT32_SUPPORT 118 | typedef uint32_t cluster_t; 119 | #else 120 | typedef uint16_t cluster_t; 121 | #endif 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif 128 | 129 | -------------------------------------------------------------------------------- /sd-reader/partition.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #include "byteordering.h" 12 | #include "partition.h" 13 | #include "partition_config.h" 14 | #include "sd-reader_config.h" 15 | 16 | #include 17 | 18 | #if USE_DYNAMIC_MEMORY 19 | #include 20 | #endif 21 | 22 | /** 23 | * \addtogroup partition Partition table support 24 | * 25 | * Support for reading partition tables and access to partitions. 26 | * 27 | * @{ 28 | */ 29 | /** 30 | * \file 31 | * Partition table implementation (license: GPLv2 or LGPLv2.1) 32 | * 33 | * \author Roland Riegel 34 | */ 35 | 36 | /** 37 | * \addtogroup partition_config Configuration of partition table support 38 | * Preprocessor defines to configure the partition support. 39 | */ 40 | 41 | #if !USE_DYNAMIC_MEMORY 42 | static struct partition_struct partition_handles[PARTITION_COUNT]; 43 | #endif 44 | 45 | /** 46 | * Opens a partition. 47 | * 48 | * Opens a partition by its index number and returns a partition 49 | * handle which describes the opened partition. 50 | * 51 | * \note This function does not support extended partitions. 52 | * 53 | * \param[in] device_read A function pointer which is used to read from the disk. 54 | * \param[in] device_read_interval A function pointer which is used to read in constant intervals from the disk. 55 | * \param[in] device_write A function pointer which is used to write to the disk. 56 | * \param[in] device_write_interval A function pointer which is used to write a data stream to disk. 57 | * \param[in] index The index of the partition which should be opened, range 0 to 3. 58 | * A negative value is allowed as well. In this case, the partition opened is 59 | * not checked for existance, begins at offset zero, has a length of zero 60 | * and is of an unknown type. Use this in case you want to open the whole device 61 | * as a single partition (e.g. for "super floppy" use). 62 | * \returns 0 on failure, a partition descriptor on success. 63 | * \see partition_close 64 | */ 65 | struct partition_struct* partition_open(device_read_t device_read, device_read_interval_t device_read_interval, device_write_t device_write, device_write_interval_t device_write_interval, int8_t index) 66 | { 67 | struct partition_struct* new_partition = 0; 68 | uint8_t buffer[0x10]; 69 | 70 | if(!device_read || !device_read_interval || index >= 4) 71 | return 0; 72 | 73 | if(index >= 0) 74 | { 75 | /* read specified partition table index */ 76 | if(!device_read(0x01be + index * 0x10, buffer, sizeof(buffer))) 77 | return 0; 78 | 79 | /* abort on empty partition entry */ 80 | if(buffer[4] == 0x00) 81 | return 0; 82 | } 83 | 84 | /* allocate partition descriptor */ 85 | #if USE_DYNAMIC_MEMORY 86 | new_partition = malloc(sizeof(*new_partition)); 87 | if(!new_partition) 88 | return 0; 89 | #else 90 | new_partition = partition_handles; 91 | uint8_t i; 92 | for(i = 0; i < PARTITION_COUNT; ++i) 93 | { 94 | if(new_partition->type == PARTITION_TYPE_FREE) 95 | break; 96 | 97 | ++new_partition; 98 | } 99 | if(i >= PARTITION_COUNT) 100 | return 0; 101 | #endif 102 | 103 | memset(new_partition, 0, sizeof(*new_partition)); 104 | 105 | /* fill partition descriptor */ 106 | new_partition->device_read = device_read; 107 | new_partition->device_read_interval = device_read_interval; 108 | new_partition->device_write = device_write; 109 | new_partition->device_write_interval = device_write_interval; 110 | 111 | if(index >= 0) 112 | { 113 | new_partition->type = buffer[4]; 114 | new_partition->offset = read32(&buffer[8]); 115 | new_partition->length = read32(&buffer[12]); 116 | } 117 | else 118 | { 119 | new_partition->type = 0xff; 120 | } 121 | 122 | return new_partition; 123 | } 124 | 125 | /** 126 | * Closes a partition. 127 | * 128 | * This function destroys a partition descriptor which was 129 | * previously obtained from a call to partition_open(). 130 | * When this function returns, the given descriptor will be 131 | * invalid. 132 | * 133 | * \param[in] partition The partition descriptor to destroy. 134 | * \returns 0 on failure, 1 on success. 135 | * \see partition_open 136 | */ 137 | uint8_t partition_close(struct partition_struct* partition) 138 | { 139 | if(!partition) 140 | return 0; 141 | 142 | /* destroy partition descriptor */ 143 | #if USE_DYNAMIC_MEMORY 144 | free(partition); 145 | #else 146 | partition->type = PARTITION_TYPE_FREE; 147 | #endif 148 | 149 | return 1; 150 | } 151 | 152 | /** 153 | * @} 154 | */ 155 | 156 | -------------------------------------------------------------------------------- /sd-reader/partition.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef PARTITION_H 12 | #define PARTITION_H 13 | 14 | #include 15 | #include "sd_raw_config.h" 16 | #include "partition_config.h" 17 | 18 | #ifdef __cplusplus 19 | extern "C" 20 | { 21 | #endif 22 | 23 | /** 24 | * \addtogroup partition 25 | * 26 | * @{ 27 | */ 28 | /** 29 | * \file 30 | * Partition table header (license: GPLv2 or LGPLv2.1) 31 | * 32 | * \author Roland Riegel 33 | */ 34 | 35 | /** 36 | * The partition table entry is not used. 37 | */ 38 | #define PARTITION_TYPE_FREE 0x00 39 | /** 40 | * The partition contains a FAT12 filesystem. 41 | */ 42 | #define PARTITION_TYPE_FAT12 0x01 43 | /** 44 | * The partition contains a FAT16 filesystem with 32MB maximum. 45 | */ 46 | #define PARTITION_TYPE_FAT16_32MB 0x04 47 | /** 48 | * The partition is an extended partition with its own partition table. 49 | */ 50 | #define PARTITION_TYPE_EXTENDED 0x05 51 | /** 52 | * The partition contains a FAT16 filesystem. 53 | */ 54 | #define PARTITION_TYPE_FAT16 0x06 55 | /** 56 | * The partition contains a FAT32 filesystem. 57 | */ 58 | #define PARTITION_TYPE_FAT32 0x0b 59 | /** 60 | * The partition contains a FAT32 filesystem with LBA. 61 | */ 62 | #define PARTITION_TYPE_FAT32_LBA 0x0c 63 | /** 64 | * The partition contains a FAT16 filesystem with LBA. 65 | */ 66 | #define PARTITION_TYPE_FAT16_LBA 0x0e 67 | /** 68 | * The partition is an extended partition with LBA. 69 | */ 70 | #define PARTITION_TYPE_EXTENDED_LBA 0x0f 71 | /** 72 | * The partition has an unknown type. 73 | */ 74 | #define PARTITION_TYPE_UNKNOWN 0xff 75 | 76 | /** 77 | * A function pointer used to read from the partition. 78 | * 79 | * \param[in] offset The offset on the device where to start reading. 80 | * \param[out] buffer The buffer into which to place the data. 81 | * \param[in] length The count of bytes to read. 82 | */ 83 | typedef uint8_t (*device_read_t)(offset_t offset, uint8_t* buffer, uintptr_t length); 84 | /** 85 | * A function pointer passed to a \c device_read_interval_t. 86 | * 87 | * \param[in] buffer The buffer which contains the data just read. 88 | * \param[in] offset The offset from which the data in \c buffer was read. 89 | * \param[in] p An opaque pointer. 90 | * \see device_read_interval_t 91 | */ 92 | typedef uint8_t (*device_read_callback_t)(uint8_t* buffer, offset_t offset, void* p); 93 | /** 94 | * A function pointer used to continuously read units of \c interval bytes 95 | * and call a callback function. 96 | * 97 | * This function starts reading at the specified offset. Every \c interval bytes, 98 | * it calls the callback function with the associated data buffer. 99 | * 100 | * By returning zero, the callback may stop reading. 101 | * 102 | * \param[in] offset Offset from which to start reading. 103 | * \param[in] buffer Pointer to a buffer which is at least interval bytes in size. 104 | * \param[in] interval Number of bytes to read before calling the callback function. 105 | * \param[in] length Number of bytes to read altogether. 106 | * \param[in] callback The function to call every interval bytes. 107 | * \param[in] p An opaque pointer directly passed to the callback function. 108 | * \returns 0 on failure, 1 on success 109 | * \see device_read_t 110 | */ 111 | typedef uint8_t (*device_read_interval_t)(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, device_read_callback_t callback, void* p); 112 | /** 113 | * A function pointer used to write to the partition. 114 | * 115 | * \param[in] offset The offset on the device where to start writing. 116 | * \param[in] buffer The buffer which to write. 117 | * \param[in] length The count of bytes to write. 118 | */ 119 | typedef uint8_t (*device_write_t)(offset_t offset, const uint8_t* buffer, uintptr_t length); 120 | /** 121 | * A function pointer passed to a \c device_write_interval_t. 122 | * 123 | * \param[in] buffer The buffer which receives the data to write. 124 | * \param[in] offset The offset to which the data in \c buffer will be written. 125 | * \param[in] p An opaque pointer. 126 | * \returns The number of bytes put into \c buffer 127 | * \see device_write_interval_t 128 | */ 129 | typedef uintptr_t (*device_write_callback_t)(uint8_t* buffer, offset_t offset, void* p); 130 | /** 131 | * A function pointer used to continuously write a data stream obtained from 132 | * a callback function. 133 | * 134 | * This function starts writing at the specified offset. To obtain the 135 | * next bytes to write, it calls the callback function. The callback fills the 136 | * provided data buffer and returns the number of bytes it has put into the buffer. 137 | * 138 | * By returning zero, the callback may stop writing. 139 | * 140 | * \param[in] offset Offset where to start writing. 141 | * \param[in] buffer Pointer to a buffer which is used for the callback function. 142 | * \param[in] length Number of bytes to write in total. May be zero for endless writes. 143 | * \param[in] callback The function used to obtain the bytes to write. 144 | * \param[in] p An opaque pointer directly passed to the callback function. 145 | * \returns 0 on failure, 1 on success 146 | * \see device_write_t 147 | */ 148 | typedef uint8_t (*device_write_interval_t)(offset_t offset, uint8_t* buffer, uintptr_t length, device_write_callback_t callback, void* p); 149 | 150 | /** 151 | * Describes a partition. 152 | */ 153 | struct partition_struct 154 | { 155 | /** 156 | * The function which reads data from the partition. 157 | * 158 | * \note The offset given to this function is relative to the whole disk, 159 | * not to the start of the partition. 160 | */ 161 | device_read_t device_read; 162 | /** 163 | * The function which repeatedly reads a constant amount of data from the partition. 164 | * 165 | * \note The offset given to this function is relative to the whole disk, 166 | * not to the start of the partition. 167 | */ 168 | device_read_interval_t device_read_interval; 169 | /** 170 | * The function which writes data to the partition. 171 | * 172 | * \note The offset given to this function is relative to the whole disk, 173 | * not to the start of the partition. 174 | */ 175 | device_write_t device_write; 176 | /** 177 | * The function which repeatedly writes data to the partition. 178 | * 179 | * \note The offset given to this function is relative to the whole disk, 180 | * not to the start of the partition. 181 | */ 182 | device_write_interval_t device_write_interval; 183 | 184 | /** 185 | * The type of the partition. 186 | * 187 | * Compare this value to the PARTITION_TYPE_* constants. 188 | */ 189 | uint8_t type; 190 | /** 191 | * The offset in blocks on the disk where this partition starts. 192 | */ 193 | uint32_t offset; 194 | /** 195 | * The length in blocks of this partition. 196 | */ 197 | uint32_t length; 198 | }; 199 | 200 | struct partition_struct* partition_open(device_read_t device_read, device_read_interval_t device_read_interval, device_write_t device_write, device_write_interval_t device_write_interval, int8_t index); 201 | uint8_t partition_close(struct partition_struct* partition); 202 | 203 | /** 204 | * @} 205 | */ 206 | 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | 211 | #endif 212 | 213 | -------------------------------------------------------------------------------- /sd-reader/partition_config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef PARTITION_CONFIG_H 12 | #define PARTITION_CONFIG_H 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | /** 20 | * \addtogroup partition 21 | * 22 | * @{ 23 | */ 24 | /** 25 | * \file 26 | * Partition configuration (license: GPLv2 or LGPLv2.1) 27 | */ 28 | 29 | /** 30 | * \ingroup partition_config 31 | * Maximum number of partition handles. 32 | */ 33 | #define PARTITION_COUNT 1 34 | 35 | /** 36 | * @} 37 | */ 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /sd-reader/sd-reader_config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef SD_READER_CONFIG_H 12 | #define SD_READER_CONFIG_H 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif 18 | 19 | /** 20 | * \addtogroup config Sd-reader configuration 21 | * 22 | * @{ 23 | */ 24 | 25 | /** 26 | * \file 27 | * Common sd-reader configuration used by all modules (license: GPLv2 or LGPLv2.1) 28 | * 29 | * \note This file contains only configuration items relevant to 30 | * all sd-reader implementation files. For module specific configuration 31 | * options, please see the files fat_config.h, partition_config.h 32 | * and sd_raw_config.h. 33 | */ 34 | 35 | /** 36 | * Controls allocation of memory. 37 | * 38 | * Set to 1 to use malloc()/free() for allocation of structures 39 | * like file and directory handles, set to 0 to use pre-allocated 40 | * fixed-size handle arrays. 41 | */ 42 | #define USE_DYNAMIC_MEMORY 0 43 | 44 | /** 45 | * @} 46 | */ 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /sd-reader/sd_manager.c: -------------------------------------------------------------------------------- 1 | /* 2 | Includes code from the LUFA Library, with the following 3 | copyright information: 4 | 5 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 6 | 7 | Permission to use, copy, modify, distribute, and sell this 8 | software and its documentation for any purpose is hereby granted 9 | without fee, provided that the above copyright notice appear in 10 | all copies and that both that the copyright notice and this 11 | permission notice and warranty disclaimer appear in supporting 12 | documentation, and that the name of the author not be used in 13 | advertising or publicity pertaining to distribution of the 14 | software without specific, written prior permission. 15 | 16 | The author disclaims all warranties with regard to this 17 | software, including all implied warranties of merchantability 18 | and fitness. In no event shall the author be liable for any 19 | special, indirect or consequential damages or any damages 20 | whatsoever resulting from loss of use, data or profits, whether 21 | in an action of contract, negligence or other tortious action, 22 | arising out of or in connection with the use or performance of 23 | this software. 24 | */ 25 | 26 | #include "sd_manager.h" 27 | #include "sd_raw.h" 28 | #include "scsi.h" 29 | 30 | uint8_t sd_read_block_callback(uint8_t* buffer, offset_t offset, void* p) 31 | { 32 | uint16_t count = 0; 33 | while(Endpoint_Write_Stream_LE( 34 | buffer, VIRTUAL_MEMORY_BLOCK_SIZE, &count) 35 | == ENDPOINT_RWSTREAM_IncompleteTransfer); 36 | return 1; 37 | } 38 | 39 | void sd_read_blocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 40 | offset_t BlockAddress, uint16_t TotalBlocks) 41 | { 42 | /* Wait until endpoint is ready before continuing */ 43 | if (Endpoint_WaitUntilReady()) 44 | return; 45 | 46 | sd_raw_read_blocks(BlockAddress << VIRTUAL_MEMORY_BLOCK_SHIFT, 47 | TotalBlocks, &sd_read_block_callback, NULL); 48 | 49 | /* If the endpoint is full, send its contents to the host */ 50 | if (!(Endpoint_IsReadWriteAllowed())) 51 | Endpoint_ClearIN(); 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | uintptr_t sd_write_block_callback(uint8_t* buffer, offset_t offset, void* p) 57 | { 58 | uint16_t count = 0; 59 | while(Endpoint_Read_Stream_LE( 60 | buffer, VIRTUAL_MEMORY_BLOCK_SIZE, &count) 61 | == ENDPOINT_RWSTREAM_IncompleteTransfer); 62 | return VIRTUAL_MEMORY_BLOCK_SIZE; 63 | } 64 | 65 | void sd_write_blocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 66 | offset_t BlockAddress, uint16_t TotalBlocks) 67 | { 68 | /* Wait until endpoint is ready before continuing */ 69 | if (Endpoint_WaitUntilReady()) 70 | return; 71 | 72 | sd_raw_write_blocks(BlockAddress << VIRTUAL_MEMORY_BLOCK_SHIFT, 73 | TotalBlocks, &sd_write_block_callback, NULL); 74 | 75 | /* If the endpoint is empty, clear it ready for the next packet from the host */ 76 | if (!(Endpoint_IsReadWriteAllowed())) 77 | Endpoint_ClearOUT(); 78 | } 79 | 80 | //////////////////////////////////////////////////////////////////////////////// 81 | 82 | uint32_t sd_get_blocks(void) 83 | { 84 | struct sd_raw_info info; 85 | sd_raw_get_info(&info); 86 | 87 | return info.capacity >> VIRTUAL_MEMORY_BLOCK_SHIFT; 88 | } 89 | -------------------------------------------------------------------------------- /sd-reader/sd_manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | Includes code from the LUFA Library, with the following 3 | copyright information: 4 | 5 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 6 | 7 | Permission to use, copy, modify, distribute, and sell this 8 | software and its documentation for any purpose is hereby granted 9 | without fee, provided that the above copyright notice appear in 10 | all copies and that both that the copyright notice and this 11 | permission notice and warranty disclaimer appear in supporting 12 | documentation, and that the name of the author not be used in 13 | advertising or publicity pertaining to distribution of the 14 | software without specific, written prior permission. 15 | 16 | The author disclaims all warranties with regard to this 17 | software, including all implied warranties of merchantability 18 | and fitness. In no event shall the author be liable for any 19 | special, indirect or consequential damages or any damages 20 | whatsoever resulting from loss of use, data or profits, whether 21 | in an action of contract, negligence or other tortious action, 22 | arising out of or in connection with the use or performance of 23 | this software. 24 | */ 25 | 26 | #ifndef SD_MANAGER_H 27 | #define SD_MANAGER_H 28 | 29 | #include 30 | #include "sd_raw.h" 31 | 32 | void sd_read_blocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 33 | offset_t BlockAddress, uint16_t TotalBlocks); 34 | void sd_write_blocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo, 35 | offset_t BlockAddress, uint16_t TotalBlocks); 36 | 37 | uint32_t sd_get_blocks(void); 38 | #endif 39 | -------------------------------------------------------------------------------- /sd-reader/sd_raw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Original copyright (c) 2006-2012 by Roland Riegel 3 | * 4 | * Modifications copyright (c) 2014 by Matthew Keeter 5 | * (in particular, the sd_raw_cache_block, sd_raw_read_blocks, and 6 | * sd_raw_write_blocks functions) 7 | * 8 | * This file is free software; you can redistribute it and/or modify 9 | * it under the terms of either the GNU General Public License version 2 10 | * or the GNU Lesser General Public License version 2.1, both as 11 | * published by the Free Software Foundation. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include "sd_raw.h" 18 | 19 | /** 20 | * \addtogroup sd_raw MMC/SD/SDHC card raw access 21 | * 22 | * This module implements read and write access to MMC, SD 23 | * and SDHC cards. It serves as a low-level driver for the 24 | * higher level modules such as partition and file system 25 | * access. 26 | * 27 | * @{ 28 | */ 29 | /** 30 | * \file 31 | * MMC/SD/SDHC raw access implementation (license: GPLv2 or LGPLv2.1) 32 | * 33 | * \author Roland Riegel 34 | */ 35 | 36 | /** 37 | * \addtogroup sd_raw_config MMC/SD configuration 38 | * Preprocessor defines to configure the MMC/SD support. 39 | */ 40 | 41 | /** 42 | * @} 43 | */ 44 | 45 | /* commands available in SPI mode */ 46 | 47 | /* CMD0: response R1 */ 48 | #define CMD_GO_IDLE_STATE 0x00 49 | /* CMD1: response R1 */ 50 | #define CMD_SEND_OP_COND 0x01 51 | /* CMD8: response R7 */ 52 | #define CMD_SEND_IF_COND 0x08 53 | /* CMD9: response R1 */ 54 | #define CMD_SEND_CSD 0x09 55 | /* CMD10: response R1 */ 56 | #define CMD_SEND_CID 0x0a 57 | /* CMD12: response R1 */ 58 | #define CMD_STOP_TRANSMISSION 0x0c 59 | /* CMD13: response R2 */ 60 | #define CMD_SEND_STATUS 0x0d 61 | /* CMD16: arg0[31:0]: block length, response R1 */ 62 | #define CMD_SET_BLOCKLEN 0x10 63 | /* CMD17: arg0[31:0]: data address, response R1 */ 64 | #define CMD_READ_SINGLE_BLOCK 0x11 65 | /* CMD18: arg0[31:0]: data address, response R1 */ 66 | #define CMD_READ_MULTIPLE_BLOCK 0x12 67 | /* CMD24: arg0[31:0]: data address, response R1 */ 68 | #define CMD_WRITE_SINGLE_BLOCK 0x18 69 | /* CMD25: arg0[31:0]: data address, response R1 */ 70 | #define CMD_WRITE_MULTIPLE_BLOCK 0x19 71 | /* CMD27: response R1 */ 72 | #define CMD_PROGRAM_CSD 0x1b 73 | /* CMD28: arg0[31:0]: data address, response R1b */ 74 | #define CMD_SET_WRITE_PROT 0x1c 75 | /* CMD29: arg0[31:0]: data address, response R1b */ 76 | #define CMD_CLR_WRITE_PROT 0x1d 77 | /* CMD30: arg0[31:0]: write protect data address, response R1 */ 78 | #define CMD_SEND_WRITE_PROT 0x1e 79 | /* CMD32: arg0[31:0]: data address, response R1 */ 80 | #define CMD_TAG_SECTOR_START 0x20 81 | /* CMD33: arg0[31:0]: data address, response R1 */ 82 | #define CMD_TAG_SECTOR_END 0x21 83 | /* CMD34: arg0[31:0]: data address, response R1 */ 84 | #define CMD_UNTAG_SECTOR 0x22 85 | /* CMD35: arg0[31:0]: data address, response R1 */ 86 | #define CMD_TAG_ERASE_GROUP_START 0x23 87 | /* CMD36: arg0[31:0]: data address, response R1 */ 88 | #define CMD_TAG_ERASE_GROUP_END 0x24 89 | /* CMD37: arg0[31:0]: data address, response R1 */ 90 | #define CMD_UNTAG_ERASE_GROUP 0x25 91 | /* CMD38: arg0[31:0]: stuff bits, response R1b */ 92 | #define CMD_ERASE 0x26 93 | /* ACMD41: arg0[31:0]: OCR contents, response R1 */ 94 | #define CMD_SD_SEND_OP_COND 0x29 95 | /* CMD42: arg0[31:0]: stuff bits, response R1b */ 96 | #define CMD_LOCK_UNLOCK 0x2a 97 | /* CMD55: arg0[31:0]: stuff bits, response R1 */ 98 | #define CMD_APP 0x37 99 | /* CMD58: arg0[31:0]: stuff bits, response R3 */ 100 | #define CMD_READ_OCR 0x3a 101 | /* CMD59: arg0[31:1]: stuff bits, arg0[0:0]: crc option, response R1 */ 102 | #define CMD_CRC_ON_OFF 0x3b 103 | 104 | /* command responses */ 105 | /* R1: size 1 byte */ 106 | #define R1_IDLE_STATE 0 107 | #define R1_ERASE_RESET 1 108 | #define R1_ILL_COMMAND 2 109 | #define R1_COM_CRC_ERR 3 110 | #define R1_ERASE_SEQ_ERR 4 111 | #define R1_ADDR_ERR 5 112 | #define R1_PARAM_ERR 6 113 | /* R1b: equals R1, additional busy bytes */ 114 | /* R2: size 2 bytes */ 115 | #define R2_CARD_LOCKED 0 116 | #define R2_WP_ERASE_SKIP 1 117 | #define R2_ERR 2 118 | #define R2_CARD_ERR 3 119 | #define R2_CARD_ECC_FAIL 4 120 | #define R2_WP_VIOLATION 5 121 | #define R2_INVAL_ERASE 6 122 | #define R2_OUT_OF_RANGE 7 123 | #define R2_CSD_OVERWRITE 7 124 | #define R2_IDLE_STATE (R1_IDLE_STATE + 8) 125 | #define R2_ERASE_RESET (R1_ERASE_RESET + 8) 126 | #define R2_ILL_COMMAND (R1_ILL_COMMAND + 8) 127 | #define R2_COM_CRC_ERR (R1_COM_CRC_ERR + 8) 128 | #define R2_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 8) 129 | #define R2_ADDR_ERR (R1_ADDR_ERR + 8) 130 | #define R2_PARAM_ERR (R1_PARAM_ERR + 8) 131 | /* R3: size 5 bytes */ 132 | #define R3_OCR_MASK (0xffffffffUL) 133 | #define R3_IDLE_STATE (R1_IDLE_STATE + 32) 134 | #define R3_ERASE_RESET (R1_ERASE_RESET + 32) 135 | #define R3_ILL_COMMAND (R1_ILL_COMMAND + 32) 136 | #define R3_COM_CRC_ERR (R1_COM_CRC_ERR + 32) 137 | #define R3_ERASE_SEQ_ERR (R1_ERASE_SEQ_ERR + 32) 138 | #define R3_ADDR_ERR (R1_ADDR_ERR + 32) 139 | #define R3_PARAM_ERR (R1_PARAM_ERR + 32) 140 | /* Data Response: size 1 byte */ 141 | #define DR_STATUS_MASK 0x0e 142 | #define DR_STATUS_ACCEPTED 0x05 143 | #define DR_STATUS_CRC_ERR 0x0a 144 | #define DR_STATUS_WRITE_ERR 0x0c 145 | 146 | /* status bits for card types */ 147 | #define SD_RAW_SPEC_1 0 148 | #define SD_RAW_SPEC_2 1 149 | #define SD_RAW_SPEC_SDHC 2 150 | 151 | #if !SD_RAW_SAVE_RAM 152 | /* static data buffer for acceleration */ 153 | uint8_t sd_raw_block[512]; 154 | /* offset where the data within sd_raw_block lies on the card */ 155 | offset_t sd_raw_block_address; 156 | #if SD_RAW_WRITE_BUFFERING 157 | /* flag to remember if raw_block was written to the card */ 158 | uint8_t sd_raw_block_written; 159 | #endif 160 | #endif 161 | 162 | /* card type state */ 163 | static uint8_t sd_raw_card_type; 164 | 165 | /* private helper functions */ 166 | static uint8_t sd_raw_rec_byte(void) ATTR_ALWAYS_INLINE; 167 | static void sd_raw_ignore_byte(void) ATTR_ALWAYS_INLINE; 168 | static uint8_t sd_raw_send_command(uint8_t command, uint32_t arg); 169 | 170 | 171 | /** 172 | * \ingroup sd_raw 173 | * Initializes memory card communication. 174 | * 175 | * \returns 0 on failure, 1 on success. 176 | */ 177 | uint8_t sd_raw_init(void) 178 | { 179 | /* enable inputs for reading card status */ 180 | configure_pin_available(); 181 | configure_pin_locked(); 182 | 183 | /* enable outputs for MOSI, SCK, SS, input for MISO */ 184 | configure_pin_mosi(); 185 | configure_pin_sck(); 186 | configure_pin_ss(); 187 | configure_pin_miso(); 188 | 189 | unselect_card(); 190 | 191 | /* initialize SPI with lowest frequency; max. 400kHz during identification mode of card */ 192 | SPCR = (0 << SPIE) | /* SPI Interrupt Enable */ 193 | (1 << SPE) | /* SPI Enable */ 194 | (0 << DORD) | /* Data Order: MSB first */ 195 | (1 << MSTR) | /* Master mode */ 196 | (0 << CPOL) | /* Clock Polarity: SCK low when idle */ 197 | (0 << CPHA) | /* Clock Phase: sample on rising SCK edge */ 198 | (1 << SPR1) | /* Clock Frequency: f_OSC / 128 */ 199 | (1 << SPR0); 200 | SPSR &= ~(1 << SPI2X); /* No doubled clock frequency */ 201 | 202 | /* initialization procedure */ 203 | sd_raw_card_type = 0; 204 | 205 | if(!sd_raw_available()) 206 | return 0; 207 | 208 | /* card needs 74 cycles minimum to start up */ 209 | for(uint8_t i = 0; i < 10; ++i) 210 | { 211 | /* wait 8 clock cycles */ 212 | sd_raw_ignore_byte(); 213 | } 214 | 215 | /* address card */ 216 | select_card(); 217 | 218 | /* reset card */ 219 | uint8_t response; 220 | for(uint16_t i = 0; ; ++i) 221 | { 222 | response = sd_raw_send_command(CMD_GO_IDLE_STATE, 0); 223 | if(response == (1 << R1_IDLE_STATE)) 224 | break; 225 | 226 | if(i == 0x1ff) 227 | { 228 | unselect_card(); 229 | return 0; 230 | } 231 | } 232 | 233 | #if SD_RAW_SDHC 234 | /* check for version of SD card specification */ 235 | response = sd_raw_send_command(CMD_SEND_IF_COND, 0x100 /* 2.7V - 3.6V */ | 0xaa /* test pattern */); 236 | if((response & (1 << R1_ILL_COMMAND)) == 0) 237 | { 238 | sd_raw_ignore_byte(); 239 | sd_raw_ignore_byte(); 240 | if((sd_raw_rec_byte() & 0x01) == 0) 241 | return 0; /* card operation voltage range doesn't match */ 242 | if(sd_raw_rec_byte() != 0xaa) 243 | return 0; /* wrong test pattern */ 244 | 245 | /* card conforms to SD 2 card specification */ 246 | sd_raw_card_type |= (1 << SD_RAW_SPEC_2); 247 | } 248 | else 249 | #endif 250 | { 251 | /* determine SD/MMC card type */ 252 | sd_raw_send_command(CMD_APP, 0); 253 | response = sd_raw_send_command(CMD_SD_SEND_OP_COND, 0); 254 | if((response & (1 << R1_ILL_COMMAND)) == 0) 255 | { 256 | /* card conforms to SD 1 card specification */ 257 | sd_raw_card_type |= (1 << SD_RAW_SPEC_1); 258 | } 259 | else 260 | { 261 | /* MMC card */ 262 | } 263 | } 264 | 265 | /* wait for card to get ready */ 266 | for(uint16_t i = 0; ; ++i) 267 | { 268 | if(sd_raw_card_type & ((1 << SD_RAW_SPEC_1) | (1 << SD_RAW_SPEC_2))) 269 | { 270 | uint32_t arg = 0; 271 | #if SD_RAW_SDHC 272 | if(sd_raw_card_type & (1 << SD_RAW_SPEC_2)) 273 | arg = 0x40000000; 274 | #endif 275 | sd_raw_send_command(CMD_APP, 0); 276 | response = sd_raw_send_command(CMD_SD_SEND_OP_COND, arg); 277 | } 278 | else 279 | { 280 | response = sd_raw_send_command(CMD_SEND_OP_COND, 0); 281 | } 282 | 283 | if((response & (1 << R1_IDLE_STATE)) == 0) 284 | break; 285 | 286 | if(i == 0x7fff) 287 | { 288 | unselect_card(); 289 | return 0; 290 | } 291 | } 292 | 293 | #if SD_RAW_SDHC 294 | if(sd_raw_card_type & (1 << SD_RAW_SPEC_2)) 295 | { 296 | if(sd_raw_send_command(CMD_READ_OCR, 0)) 297 | { 298 | unselect_card(); 299 | return 0; 300 | } 301 | 302 | if(sd_raw_rec_byte() & 0x40) 303 | sd_raw_card_type |= (1 << SD_RAW_SPEC_SDHC); 304 | 305 | sd_raw_ignore_byte(); 306 | sd_raw_ignore_byte(); 307 | sd_raw_ignore_byte(); 308 | } 309 | #endif 310 | 311 | /* set block size to 512 bytes */ 312 | if(sd_raw_send_command(CMD_SET_BLOCKLEN, 512)) 313 | { 314 | unselect_card(); 315 | return 0; 316 | } 317 | 318 | /* deaddress card */ 319 | unselect_card(); 320 | 321 | /* switch to highest SPI frequency possible */ 322 | SPCR &= ~((1 << SPR1) | (1 << SPR0)); /* Clock Frequency: f_OSC / 4 */ 323 | SPSR |= (1 << SPI2X); /* Doubled Clock Frequency: f_OSC / 2 */ 324 | 325 | #if !SD_RAW_SAVE_RAM 326 | /* the first block is likely to be accessed first, so precache it here */ 327 | sd_raw_block_address = (offset_t) -1; 328 | #if SD_RAW_WRITE_BUFFERING 329 | sd_raw_block_written = 1; 330 | #endif 331 | if(!sd_raw_read(0, sd_raw_block, sizeof(sd_raw_block))) 332 | return 0; 333 | #endif 334 | 335 | return 1; 336 | } 337 | 338 | /** 339 | * \ingroup sd_raw 340 | * Checks wether a memory card is located in the slot. 341 | * 342 | * \returns 1 if the card is available, 0 if it is not. 343 | */ 344 | uint8_t sd_raw_available(void) 345 | { 346 | return get_pin_available() != 0x00; 347 | } 348 | 349 | /** 350 | * \ingroup sd_raw 351 | * Checks wether the memory card is locked for write access. 352 | * 353 | * \returns 1 if the card is locked, 0 if it is not. 354 | */ 355 | uint8_t sd_raw_locked(void) 356 | { 357 | return get_pin_locked() == 0x00; 358 | } 359 | 360 | /** 361 | * \ingroup sd_raw 362 | * Receives a raw byte from the memory card. 363 | * 364 | * \returns The byte which should be read. 365 | */ 366 | static inline uint8_t sd_raw_rec_byte(void) 367 | { 368 | /* send dummy data for receiving some */ 369 | SPDR = 0xff; 370 | while(!(SPSR & (1 << SPIF))); 371 | return SPDR; 372 | } 373 | 374 | /** 375 | * \ingroup sd_raw 376 | * Ignores a raw byte from the memory card. 377 | */ 378 | static inline void sd_raw_ignore_byte(void) 379 | { 380 | /* send dummy data for receiving some */ 381 | SPDR = 0xff; 382 | while(!(SPSR & (1 << SPIF))); 383 | } 384 | 385 | /** 386 | * \ingroup sd_raw 387 | * Send a command to the memory card which responses with a R1 response (and possibly others). 388 | * 389 | * \param[in] command The command to send. 390 | * \param[in] arg The argument for command. 391 | * \returns The command answer. 392 | */ 393 | uint8_t sd_raw_send_command(uint8_t command, uint32_t arg) 394 | { 395 | uint8_t response; 396 | 397 | /* wait some clock cycles */ 398 | sd_raw_ignore_byte(); 399 | 400 | /* send command via SPI */ 401 | SPI_SendByte(0x40 | command); 402 | SPI_SendByte((arg >> 24) & 0xff); 403 | SPI_SendByte((arg >> 16) & 0xff); 404 | SPI_SendByte((arg >> 8) & 0xff); 405 | SPI_SendByte((arg >> 0) & 0xff); 406 | switch(command) 407 | { 408 | case CMD_GO_IDLE_STATE: 409 | SPI_SendByte(0x95); 410 | break; 411 | case CMD_SEND_IF_COND: 412 | SPI_SendByte(0x87); 413 | break; 414 | default: 415 | SPI_SendByte(0xff); 416 | break; 417 | } 418 | 419 | /* receive response */ 420 | for(uint8_t i = 0; i < 10; ++i) 421 | { 422 | response = sd_raw_rec_byte(); 423 | if(response != 0xff) 424 | break; 425 | } 426 | 427 | return response; 428 | } 429 | 430 | 431 | /** 432 | * \ingroup sd_raw 433 | * Caches a block of data from the card. 434 | * 435 | * \param[in] block_address The block to read. 436 | * \returns 0 on failure, 1 on success. 437 | */ 438 | #if SD_RAW_WRITE_BUFFERING && !SD_RAW_SAVE_RAM 439 | uint8_t sd_raw_cache_block(offset_t block_address) 440 | { 441 | /* If we've already cached this data, then return 1. */ 442 | if (sd_raw_block_address == block_address) 443 | return 1; 444 | /* Otherwise, write the in-RAM raw block to the card. */ 445 | else if(!sd_raw_sync()) 446 | return 0; 447 | 448 | /* address card */ 449 | select_card(); 450 | 451 | /* send single block request */ 452 | #if SD_RAW_SDHC 453 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address))) 454 | #else 455 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, block_address)) 456 | #endif 457 | { 458 | unselect_card(); 459 | return 0; 460 | } 461 | 462 | /* wait for data block (start byte 0xfe) */ 463 | while(sd_raw_rec_byte() != 0xfe); 464 | 465 | /* read byte block */ 466 | uint8_t* cache = sd_raw_block; 467 | for(uint16_t i = 0; i < 512; ++i) 468 | *cache++ = sd_raw_rec_byte(); 469 | sd_raw_block_address = block_address; 470 | 471 | /* read crc16 */ 472 | sd_raw_ignore_byte(); 473 | sd_raw_ignore_byte(); 474 | 475 | /* deaddress card */ 476 | unselect_card(); 477 | 478 | /* let card some time to finish */ 479 | sd_raw_ignore_byte(); 480 | 481 | return 1; 482 | } 483 | #endif 484 | 485 | /** 486 | * \ingroup sd_raw 487 | * Reads raw data from the card. 488 | * 489 | * \param[in] offset The offset from which to read. 490 | * \param[out] buffer The buffer into which to write the data. 491 | * \param[in] length The number of bytes to read. 492 | * \returns 0 on failure, 1 on success. 493 | * \see sd_raw_read_interval, sd_raw_write, sd_raw_write_interval 494 | */ 495 | uint8_t sd_raw_read(offset_t offset, uint8_t* buffer, uintptr_t length) 496 | { 497 | offset_t block_address; 498 | uint16_t block_offset; 499 | uint16_t read_length; 500 | while(length > 0) 501 | { 502 | /* determine byte count to read at once */ 503 | block_offset = offset & 0x01ff; 504 | block_address = offset - block_offset; 505 | read_length = 512 - block_offset; /* read up to block border */ 506 | if(read_length > length) 507 | read_length = length; 508 | 509 | #if !SD_RAW_SAVE_RAM 510 | /* check if the requested data is cached */ 511 | if(block_address != sd_raw_block_address) 512 | #endif 513 | { 514 | #if SD_RAW_WRITE_BUFFERING 515 | if(!sd_raw_sync()) 516 | return 0; 517 | #endif 518 | 519 | /* address card */ 520 | select_card(); 521 | 522 | /* send single block request */ 523 | #if SD_RAW_SDHC 524 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address))) 525 | #else 526 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, block_address)) 527 | #endif 528 | { 529 | unselect_card(); 530 | return 0; 531 | } 532 | 533 | /* wait for data block (start byte 0xfe) */ 534 | while(sd_raw_rec_byte() != 0xfe); 535 | 536 | #if SD_RAW_SAVE_RAM 537 | /* read byte block */ 538 | uint16_t read_to = block_offset + read_length; 539 | for(uint16_t i = 0; i < 512; ++i) 540 | { 541 | uint8_t b = sd_raw_rec_byte(); 542 | if(i >= block_offset && i < read_to) 543 | *buffer++ = b; 544 | } 545 | #else 546 | /* read byte block */ 547 | uint8_t* cache = sd_raw_block; 548 | for(uint16_t i = 0; i < 512; ++i) 549 | *cache++ = sd_raw_rec_byte(); 550 | sd_raw_block_address = block_address; 551 | 552 | memcpy(buffer, sd_raw_block + block_offset, read_length); 553 | buffer += read_length; 554 | #endif 555 | 556 | /* read crc16 */ 557 | sd_raw_ignore_byte(); 558 | sd_raw_ignore_byte(); 559 | 560 | /* deaddress card */ 561 | unselect_card(); 562 | 563 | /* let card some time to finish */ 564 | sd_raw_ignore_byte(); 565 | } 566 | #if !SD_RAW_SAVE_RAM 567 | else 568 | { 569 | /* use cached data */ 570 | memcpy(buffer, sd_raw_block + block_offset, read_length); 571 | buffer += read_length; 572 | } 573 | #endif 574 | 575 | length -= read_length; 576 | offset += read_length; 577 | } 578 | 579 | return 1; 580 | } 581 | 582 | /** 583 | * \ingroup sd_raw 584 | * Continuously reads units of \c interval bytes and calls a callback function. 585 | * 586 | * This function starts reading at the specified offset. Every \c interval bytes, 587 | * it calls the callback function with the associated data buffer. 588 | * 589 | * By returning zero, the callback may stop reading. 590 | * 591 | * \note Within the callback function, you can not start another read or 592 | * write operation. 593 | * \note This function only works if the following conditions are met: 594 | * - (offset - (offset % 512)) % interval == 0 595 | * - length % interval == 0 596 | * 597 | * \param[in] offset Offset from which to start reading. 598 | * \param[in] buffer Pointer to a buffer which is at least interval bytes in size. 599 | * \param[in] interval Number of bytes to read before calling the callback function. 600 | * \param[in] length Number of bytes to read altogether. 601 | * \param[in] callback The function to call every interval bytes. 602 | * \param[in] p An opaque pointer directly passed to the callback function. 603 | * \returns 0 on failure, 1 on success 604 | * \see sd_raw_write_interval, sd_raw_read, sd_raw_write 605 | */ 606 | uint8_t sd_raw_read_interval(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, sd_raw_read_interval_handler_t callback, void* p) 607 | { 608 | if(!buffer || interval == 0 || length < interval || !callback) 609 | return 0; 610 | 611 | #if !SD_RAW_SAVE_RAM 612 | while(length >= interval) 613 | { 614 | /* as reading is now buffered, we directly 615 | * hand over the request to sd_raw_read() 616 | */ 617 | if(!sd_raw_read(offset, buffer, interval)) 618 | return 0; 619 | if(!callback(buffer, offset, p)) 620 | break; 621 | offset += interval; 622 | length -= interval; 623 | } 624 | 625 | return 1; 626 | #else 627 | /* address card */ 628 | select_card(); 629 | 630 | uint16_t block_offset; 631 | uint16_t read_length; 632 | uint8_t* buffer_cur; 633 | uint8_t finished = 0; 634 | do 635 | { 636 | /* determine byte count to read at once */ 637 | block_offset = offset & 0x01ff; 638 | read_length = 512 - block_offset; 639 | 640 | /* send single block request */ 641 | #if SD_RAW_SDHC 642 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? offset / 512 : offset - block_offset))) 643 | #else 644 | if(sd_raw_send_command(CMD_READ_SINGLE_BLOCK, offset - block_offset)) 645 | #endif 646 | { 647 | unselect_card(); 648 | return 0; 649 | } 650 | 651 | /* wait for data block (start byte 0xfe) */ 652 | while(sd_raw_rec_byte() != 0xfe); 653 | 654 | /* read up to the data of interest */ 655 | for(uint16_t i = 0; i < block_offset; ++i) 656 | sd_raw_ignore_byte(); 657 | 658 | /* read interval bytes of data and execute the callback */ 659 | do 660 | { 661 | if(read_length < interval || length < interval) 662 | break; 663 | 664 | buffer_cur = buffer; 665 | for(uint16_t i = 0; i < interval; ++i) 666 | *buffer_cur++ = sd_raw_rec_byte(); 667 | 668 | if(!callback(buffer, offset + (512 - read_length), p)) 669 | { 670 | finished = 1; 671 | break; 672 | } 673 | 674 | read_length -= interval; 675 | length -= interval; 676 | 677 | } while(read_length > 0 && length > 0); 678 | 679 | /* read rest of data block */ 680 | while(read_length-- > 0) 681 | sd_raw_ignore_byte(); 682 | 683 | /* read crc16 */ 684 | sd_raw_ignore_byte(); 685 | sd_raw_ignore_byte(); 686 | 687 | if(length < interval) 688 | break; 689 | 690 | offset = offset - block_offset + 512; 691 | 692 | } while(!finished); 693 | 694 | /* deaddress card */ 695 | unselect_card(); 696 | 697 | /* let card some time to finish */ 698 | sd_raw_ignore_byte(); 699 | 700 | return 1; 701 | #endif 702 | } 703 | 704 | 705 | /** 706 | * \ingroup sd_raw 707 | * Reads multiple blocks and calls a callback function. 708 | * 709 | * This function starts reading at the specified offset. Every 512 bytes, 710 | * it calls the callback function with the associated data buffer. 711 | * 712 | * \note Within the callback function, you can not start another read or 713 | * write operation. 714 | * \note This function only works if the following conditions are met: 715 | * - offset % 512 == 0 716 | * 717 | * \param[in] offset Offset from which to start reading. 718 | * \param[in] block_count Number of blocks to read. 719 | * \param[in] callback The function to call every interval bytes. 720 | * \param[in] p An opaque pointer directly passed to the callback function. 721 | * \returns 0 on failure, 1 on success 722 | * \see sd_raw_read_interval, sd_raw_write_interval, sd_raw_read, sd_raw_write 723 | */ 724 | #if SD_RAW_WRITE_BUFFERING && !SD_RAW_SAVE_RAM 725 | uint8_t sd_raw_read_blocks(offset_t block_address, uintptr_t block_count, sd_raw_read_interval_handler_t callback, void* p) 726 | { 727 | /* If we're only reading a single block, then do manually. */ 728 | if (block_count == 1) 729 | { 730 | sd_raw_cache_block(block_address); 731 | callback(sd_raw_block, block_address, p); 732 | return 1; 733 | } 734 | 735 | /* If we've already cached the first block, process it */ 736 | if (sd_raw_block_address == block_address) 737 | { 738 | callback(sd_raw_block, block_address, p); 739 | block_address += 512; 740 | block_count--; 741 | } 742 | 743 | /* Write the in-RAM raw block to the card. */ 744 | if(!sd_raw_sync()) 745 | return 0; 746 | 747 | /* address card */ 748 | select_card(); 749 | 750 | /* send single block request */ 751 | #if SD_RAW_SDHC 752 | if(sd_raw_send_command(CMD_READ_MULTIPLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address))) 753 | #else 754 | if(sd_raw_send_command(CMD_READ_MULTIPLE_BLOCK, block_address)) 755 | #endif 756 | { 757 | unselect_card(); 758 | return 0; 759 | } 760 | 761 | for (uintptr_t b=0; b < block_count; ++b) 762 | { 763 | /* wait for data block (start byte 0xfe) */ 764 | while(sd_raw_rec_byte() != 0xfe); 765 | 766 | /* read byte block */ 767 | uint8_t* cache = sd_raw_block; 768 | for(uint16_t i = 0; i < 512; ++i) 769 | *cache++ = sd_raw_rec_byte(); 770 | sd_raw_block_address += 512; 771 | 772 | /* read crc16 */ 773 | sd_raw_ignore_byte(); 774 | sd_raw_ignore_byte(); 775 | 776 | /* Activate the callback */ 777 | callback(sd_raw_block, sd_raw_block_address, p); 778 | } 779 | 780 | /* Stop transmission */ 781 | sd_raw_send_command(CMD_STOP_TRANSMISSION, 0); 782 | 783 | /* deaddress card */ 784 | unselect_card(); 785 | 786 | /* let card some time to finish */ 787 | sd_raw_ignore_byte(); 788 | 789 | return 1; 790 | } 791 | #endif 792 | 793 | #if DOXYGEN || SD_RAW_WRITE_SUPPORT 794 | /** 795 | * \ingroup sd_raw 796 | * Writes raw data to the card. 797 | * 798 | * \note If write buffering is enabled, you might have to 799 | * call sd_raw_sync() before disconnecting the card 800 | * to ensure all remaining data has been written. 801 | * 802 | * \param[in] offset The offset where to start writing. 803 | * \param[in] buffer The buffer containing the data to be written. 804 | * \param[in] length The number of bytes to write. 805 | * \returns 0 on failure, 1 on success. 806 | * \see sd_raw_write_interval, sd_raw_read, sd_raw_read_interval 807 | */ 808 | uint8_t sd_raw_write(offset_t offset, const uint8_t* buffer, uintptr_t length) 809 | { 810 | if(sd_raw_locked()) 811 | return 0; 812 | 813 | offset_t block_address; 814 | uint16_t block_offset; 815 | uint16_t write_length; 816 | while(length > 0) 817 | { 818 | /* determine byte count to write at once */ 819 | block_offset = offset & 0x01ff; 820 | block_address = offset - block_offset; 821 | write_length = 512 - block_offset; /* write up to block border */ 822 | if(write_length > length) 823 | write_length = length; 824 | 825 | /* Merge the data to write with the content of the block. 826 | * Use the cached block if available. 827 | */ 828 | if(block_address != sd_raw_block_address) 829 | { 830 | #if SD_RAW_WRITE_BUFFERING 831 | if(!sd_raw_sync()) 832 | return 0; 833 | #endif 834 | 835 | if(block_offset || write_length < 512) 836 | { 837 | if(!sd_raw_read(block_address, sd_raw_block, sizeof(sd_raw_block))) 838 | return 0; 839 | } 840 | sd_raw_block_address = block_address; 841 | } 842 | 843 | if(buffer != sd_raw_block) 844 | { 845 | memcpy(sd_raw_block + block_offset, buffer, write_length); 846 | 847 | #if SD_RAW_WRITE_BUFFERING 848 | sd_raw_block_written = 0; 849 | 850 | if(length == write_length) 851 | return 1; 852 | #endif 853 | } 854 | 855 | /* address card */ 856 | select_card(); 857 | 858 | /* send single block request */ 859 | #if SD_RAW_SDHC 860 | if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address))) 861 | #else 862 | if(sd_raw_send_command(CMD_WRITE_SINGLE_BLOCK, block_address)) 863 | #endif 864 | { 865 | unselect_card(); 866 | return 0; 867 | } 868 | 869 | /* send start byte */ 870 | SPI_SendByte(0xfe); 871 | 872 | /* write byte block */ 873 | uint8_t* cache = sd_raw_block; 874 | for(uint16_t i = 0; i < 512; ++i) 875 | SPI_SendByte(*cache++); 876 | 877 | /* write dummy crc16 */ 878 | SPI_SendByte(0xff); 879 | SPI_SendByte(0xff); 880 | 881 | /* wait while card is busy */ 882 | while(sd_raw_rec_byte() != 0xff); 883 | sd_raw_ignore_byte(); 884 | 885 | /* deaddress card */ 886 | unselect_card(); 887 | 888 | buffer += write_length; 889 | offset += write_length; 890 | length -= write_length; 891 | 892 | #if SD_RAW_WRITE_BUFFERING 893 | sd_raw_block_written = 1; 894 | #endif 895 | } 896 | 897 | return 1; 898 | } 899 | #endif 900 | 901 | #if DOXYGEN || SD_RAW_WRITE_SUPPORT 902 | /** 903 | * \ingroup sd_raw 904 | * Writes a continuous data stream obtained from a callback function. 905 | * 906 | * This function starts writing at the specified offset. To obtain the 907 | * next bytes to write, it calls the callback function. The callback fills the 908 | * provided data buffer and returns the number of bytes it has put into the buffer. 909 | * 910 | * By returning zero, the callback may stop writing. 911 | * 912 | * \param[in] offset Offset where to start writing. 913 | * \param[in] buffer Pointer to a buffer which is used for the callback function. 914 | * \param[in] length Number of bytes to write in total. May be zero for endless writes. 915 | * \param[in] callback The function used to obtain the bytes to write. 916 | * \param[in] p An opaque pointer directly passed to the callback function. 917 | * \returns 0 on failure, 1 on success 918 | * \see sd_raw_read_interval, sd_raw_write, sd_raw_read 919 | */ 920 | uint8_t sd_raw_write_interval(offset_t offset, uint8_t* buffer, uintptr_t length, sd_raw_write_interval_handler_t callback, void* p) 921 | { 922 | #if SD_RAW_SAVE_RAM 923 | #error "SD_RAW_WRITE_SUPPORT is not supported together with SD_RAW_SAVE_RAM" 924 | #endif 925 | 926 | if(!buffer || !callback) 927 | return 0; 928 | 929 | uint8_t endless = (length == 0); 930 | while(endless || length > 0) 931 | { 932 | uint16_t bytes_to_write = callback(buffer, offset, p); 933 | if(!bytes_to_write) 934 | break; 935 | if(!endless && bytes_to_write > length) 936 | return 0; 937 | 938 | /* as writing is always buffered, we directly 939 | * hand over the request to sd_raw_write() 940 | */ 941 | if(!sd_raw_write(offset, buffer, bytes_to_write)) 942 | return 0; 943 | 944 | offset += bytes_to_write; 945 | length -= bytes_to_write; 946 | } 947 | 948 | return 1; 949 | } 950 | #endif 951 | 952 | 953 | /** 954 | * \ingroup sd_raw 955 | * Writes a continuous data stream obtained from a callback function. 956 | * 957 | * This function starts writing at the specified offset. To obtain the 958 | * next bytes to write, it calls the callback function. The callback fills the 959 | * provided data buffer with 512 bytes. 960 | * 961 | * \note Within the callback function, you can not start another read or 962 | * write operation. 963 | * \note This function only works if the following conditions are met: 964 | * - offset % 512 == 0 965 | * 966 | * \param[in] offset Offset where to start writing. 967 | * \param[in] block_count Total number of blocks to write. 968 | * \param[in] callback The function used to obtain the bytes to write. 969 | * \param[in] p An opaque pointer directly passed to the callback function. 970 | * \returns 0 on failure, 1 on success 971 | * \see sd_raw_write_interval, sd_raw_read_interval, sd_raw_write, sd_raw_read 972 | */ 973 | uint8_t sd_raw_write_blocks(offset_t block_address, uintptr_t block_count, sd_raw_write_interval_handler_t callback, void* p) 974 | { 975 | /* Write the in-RAM raw block to the card if necessary. */ 976 | if (block_address != sd_raw_block_address) 977 | sd_raw_sync(); 978 | 979 | /* If we're only writing one or two blocks, then do so manually. 980 | * (since this only necessitates a single block write) 981 | */ 982 | if (block_count <= 2) 983 | { 984 | callback(sd_raw_block, block_address, p); 985 | sd_raw_block_address = block_address; 986 | sd_raw_block_written = 0; 987 | if (block_count == 2) { 988 | sd_raw_sync(); 989 | callback(sd_raw_block, block_address, p); 990 | sd_raw_block_address += 512; 991 | sd_raw_block_written = 0; 992 | } 993 | return 1; 994 | } 995 | 996 | /* address card */ 997 | select_card(); 998 | #if SD_RAW_SDHC 999 | if(sd_raw_send_command(CMD_WRITE_MULTIPLE_BLOCK, (sd_raw_card_type & (1 << SD_RAW_SPEC_SDHC) ? block_address / 512 : block_address))) 1000 | #else 1001 | if (sd_raw_send_command(CMD_WRITE_MULTIPLE_BLOCK, block_address)) 1002 | #endif 1003 | { 1004 | unselect_card(); 1005 | return 0; 1006 | } 1007 | 1008 | sd_raw_block_address = block_address; 1009 | for (uintptr_t b=0; b < block_count - 1; ++b) 1010 | { 1011 | /* Get data from the callback. */ 1012 | callback(sd_raw_block, block_address, p); 1013 | 1014 | /* Send the 'continue transmission' token */ 1015 | SPI_SendByte(0xfc); 1016 | uint8_t* cache = sd_raw_block; 1017 | for (uint16_t i=0; i < 512; ++i) 1018 | SPI_SendByte(*cache++); 1019 | 1020 | /* write dummy crc16 */ 1021 | SPI_SendByte(0xff); 1022 | SPI_SendByte(0xff); 1023 | 1024 | /* Ignore data response token */ 1025 | sd_raw_ignore_byte(); 1026 | 1027 | /* wait while card is busy */ 1028 | while(sd_raw_rec_byte() != 0xff); 1029 | 1030 | sd_raw_block_address += 512; 1031 | } 1032 | /* Load the last block into RAM without writing to the SD card. */ 1033 | callback(sd_raw_block, block_address, p); 1034 | sd_raw_block_written = 0; 1035 | 1036 | /* Terminate the write multiple block command with the end token */ 1037 | SPI_SendByte(0xfd); 1038 | 1039 | /* wait while card is busy */ 1040 | while(sd_raw_rec_byte() != 0xff); 1041 | 1042 | return 1; 1043 | } 1044 | 1045 | #if DOXYGEN || SD_RAW_WRITE_SUPPORT 1046 | /** 1047 | * \ingroup sd_raw 1048 | * Writes the write buffer's content to the card. 1049 | * 1050 | * \note When write buffering is enabled, you should 1051 | * call this function before disconnecting the 1052 | * card to ensure all remaining data has been 1053 | * written. 1054 | * 1055 | * \returns 0 on failure, 1 on success. 1056 | * \see sd_raw_write 1057 | */ 1058 | uint8_t sd_raw_sync() 1059 | { 1060 | #if SD_RAW_WRITE_BUFFERING 1061 | if(sd_raw_block_written) 1062 | return 1; 1063 | if(!sd_raw_write(sd_raw_block_address, sd_raw_block, sizeof(sd_raw_block))) 1064 | return 0; 1065 | sd_raw_block_written = 1; 1066 | #endif 1067 | return 1; 1068 | } 1069 | #endif 1070 | 1071 | /** 1072 | * \ingroup sd_raw 1073 | * Reads informational data from the card. 1074 | * 1075 | * This function reads and returns the card's registers 1076 | * containing manufacturing and status information. 1077 | * 1078 | * \note: The information retrieved by this function is 1079 | * not required in any way to operate on the card, 1080 | * but it might be nice to display some of the data 1081 | * to the user. 1082 | * 1083 | * \param[in] info A pointer to the structure into which to save the information. 1084 | * \returns 0 on failure, 1 on success. 1085 | */ 1086 | uint8_t sd_raw_get_info(struct sd_raw_info* info) 1087 | { 1088 | if(!info || !sd_raw_available()) 1089 | return 0; 1090 | 1091 | memset(info, 0, sizeof(*info)); 1092 | 1093 | select_card(); 1094 | 1095 | /* read cid register */ 1096 | if(sd_raw_send_command(CMD_SEND_CID, 0)) 1097 | { 1098 | unselect_card(); 1099 | return 0; 1100 | } 1101 | while(sd_raw_rec_byte() != 0xfe); 1102 | for(uint8_t i = 0; i < 18; ++i) 1103 | { 1104 | uint8_t b = sd_raw_rec_byte(); 1105 | 1106 | switch(i) 1107 | { 1108 | case 0: 1109 | info->manufacturer = b; 1110 | break; 1111 | case 1: 1112 | case 2: 1113 | info->oem[i - 1] = b; 1114 | break; 1115 | case 3: 1116 | case 4: 1117 | case 5: 1118 | case 6: 1119 | case 7: 1120 | info->product[i - 3] = b; 1121 | break; 1122 | case 8: 1123 | info->revision = b; 1124 | break; 1125 | case 9: 1126 | case 10: 1127 | case 11: 1128 | case 12: 1129 | info->serial |= (uint32_t) b << ((12 - i) * 8); 1130 | break; 1131 | case 13: 1132 | info->manufacturing_year = b << 4; 1133 | break; 1134 | case 14: 1135 | info->manufacturing_year |= b >> 4; 1136 | info->manufacturing_month = b & 0x0f; 1137 | break; 1138 | } 1139 | } 1140 | 1141 | /* read csd register */ 1142 | uint8_t csd_read_bl_len = 0; 1143 | uint8_t csd_c_size_mult = 0; 1144 | #if SD_RAW_SDHC 1145 | uint16_t csd_c_size = 0; 1146 | #else 1147 | uint32_t csd_c_size = 0; 1148 | #endif 1149 | uint8_t csd_structure = 0; 1150 | if(sd_raw_send_command(CMD_SEND_CSD, 0)) 1151 | { 1152 | unselect_card(); 1153 | return 0; 1154 | } 1155 | while(sd_raw_rec_byte() != 0xfe); 1156 | for(uint8_t i = 0; i < 18; ++i) 1157 | { 1158 | uint8_t b = sd_raw_rec_byte(); 1159 | 1160 | if(i == 0) 1161 | { 1162 | csd_structure = b >> 6; 1163 | } 1164 | else if(i == 14) 1165 | { 1166 | if(b & 0x40) 1167 | info->flag_copy = 1; 1168 | if(b & 0x20) 1169 | info->flag_write_protect = 1; 1170 | if(b & 0x10) 1171 | info->flag_write_protect_temp = 1; 1172 | info->format = (b & 0x0c) >> 2; 1173 | } 1174 | else 1175 | { 1176 | #if SD_RAW_SDHC 1177 | if(csd_structure == 0x01) 1178 | { 1179 | switch(i) 1180 | { 1181 | case 7: 1182 | b &= 0x3f; 1183 | case 8: 1184 | case 9: 1185 | csd_c_size <<= 8; 1186 | csd_c_size |= b; 1187 | break; 1188 | } 1189 | if(i == 9) 1190 | { 1191 | ++csd_c_size; 1192 | info->capacity = (offset_t) csd_c_size * 512 * 1024; 1193 | } 1194 | } 1195 | else if(csd_structure == 0x00) 1196 | #endif 1197 | { 1198 | switch(i) 1199 | { 1200 | case 5: 1201 | csd_read_bl_len = b & 0x0f; 1202 | break; 1203 | case 6: 1204 | csd_c_size = b & 0x03; 1205 | csd_c_size <<= 8; 1206 | break; 1207 | case 7: 1208 | csd_c_size |= b; 1209 | csd_c_size <<= 2; 1210 | break; 1211 | case 8: 1212 | csd_c_size |= b >> 6; 1213 | ++csd_c_size; 1214 | break; 1215 | case 9: 1216 | csd_c_size_mult = b & 0x03; 1217 | csd_c_size_mult <<= 1; 1218 | break; 1219 | case 10: 1220 | csd_c_size_mult |= b >> 7; 1221 | 1222 | info->capacity = (uint32_t) csd_c_size << (csd_c_size_mult + csd_read_bl_len + 2); 1223 | break; 1224 | } 1225 | } 1226 | } 1227 | } 1228 | 1229 | unselect_card(); 1230 | 1231 | return 1; 1232 | } 1233 | 1234 | -------------------------------------------------------------------------------- /sd-reader/sd_raw.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef SD_RAW_H 12 | #define SD_RAW_H 13 | 14 | #include 15 | 16 | #include "sd_raw_config.h" 17 | 18 | #ifdef __cplusplus 19 | extern "C" 20 | { 21 | #endif 22 | 23 | /** 24 | * \addtogroup sd_raw 25 | * 26 | * @{ 27 | */ 28 | /** 29 | * \file 30 | * MMC/SD/SDHC raw access header (license: GPLv2 or LGPLv2.1) 31 | * 32 | * \author Roland Riegel 33 | */ 34 | 35 | /** 36 | * The card's layout is harddisk-like, which means it contains 37 | * a master boot record with a partition table. 38 | */ 39 | #define SD_RAW_FORMAT_HARDDISK 0 40 | /** 41 | * The card contains a single filesystem and no partition table. 42 | */ 43 | #define SD_RAW_FORMAT_SUPERFLOPPY 1 44 | /** 45 | * The card's layout follows the Universal File Format. 46 | */ 47 | #define SD_RAW_FORMAT_UNIVERSAL 2 48 | /** 49 | * The card's layout is unknown. 50 | */ 51 | #define SD_RAW_FORMAT_UNKNOWN 3 52 | 53 | /** 54 | * This struct is used by sd_raw_get_info() to return 55 | * manufacturing and status information of the card. 56 | */ 57 | struct sd_raw_info 58 | { 59 | /** 60 | * A manufacturer code globally assigned by the SD card organization. 61 | */ 62 | uint8_t manufacturer; 63 | /** 64 | * A string describing the card's OEM or content, globally assigned by the SD card organization. 65 | */ 66 | uint8_t oem[3]; 67 | /** 68 | * A product name. 69 | */ 70 | uint8_t product[6]; 71 | /** 72 | * The card's revision, coded in packed BCD. 73 | * 74 | * For example, the revision value \c 0x32 means "3.2". 75 | */ 76 | uint8_t revision; 77 | /** 78 | * A serial number assigned by the manufacturer. 79 | */ 80 | uint32_t serial; 81 | /** 82 | * The year of manufacturing. 83 | * 84 | * A value of zero means year 2000. 85 | */ 86 | uint8_t manufacturing_year; 87 | /** 88 | * The month of manufacturing. 89 | */ 90 | uint8_t manufacturing_month; 91 | /** 92 | * The card's total capacity in bytes. 93 | */ 94 | offset_t capacity; 95 | /** 96 | * Defines wether the card's content is original or copied. 97 | * 98 | * A value of \c 0 means original, \c 1 means copied. 99 | */ 100 | uint8_t flag_copy; 101 | /** 102 | * Defines wether the card's content is write-protected. 103 | * 104 | * \note This is an internal flag and does not represent the 105 | * state of the card's mechanical write-protect switch. 106 | */ 107 | uint8_t flag_write_protect; 108 | /** 109 | * Defines wether the card's content is temporarily write-protected. 110 | * 111 | * \note This is an internal flag and does not represent the 112 | * state of the card's mechanical write-protect switch. 113 | */ 114 | uint8_t flag_write_protect_temp; 115 | /** 116 | * The card's data layout. 117 | * 118 | * See the \c SD_RAW_FORMAT_* constants for details. 119 | * 120 | * \note This value is not guaranteed to match reality. 121 | */ 122 | uint8_t format; 123 | }; 124 | 125 | #if !SD_RAW_SAVE_RAM 126 | extern uint8_t sd_raw_block[512]; 127 | extern offset_t sd_raw_block_address; 128 | #if SD_RAW_WRITE_BUFFERING 129 | extern uint8_t sd_raw_block_written; 130 | #endif 131 | #endif 132 | 133 | typedef uint8_t (*sd_raw_read_interval_handler_t)(uint8_t* buffer, offset_t offset, void* p); 134 | typedef uintptr_t (*sd_raw_write_interval_handler_t)(uint8_t* buffer, offset_t offset, void* p); 135 | 136 | uint8_t sd_raw_init(void); 137 | uint8_t sd_raw_available(void); 138 | uint8_t sd_raw_locked(void); 139 | 140 | uint8_t sd_raw_read(offset_t offset, uint8_t* buffer, uintptr_t length); 141 | uint8_t sd_raw_read_interval(offset_t offset, uint8_t* buffer, uintptr_t interval, uintptr_t length, sd_raw_read_interval_handler_t callback, void* p); 142 | #if SD_RAW_WRITE_BUFFERING && !SD_RAW_SAVE_RAM 143 | uint8_t sd_raw_read_blocks(offset_t block_address, uintptr_t block_count, sd_raw_read_interval_handler_t callback, void* p); 144 | uint8_t sd_raw_write_blocks(offset_t block_address, uintptr_t block_count, sd_raw_write_interval_handler_t callback, void* p); 145 | #endif 146 | 147 | uint8_t sd_raw_write(offset_t offset, const uint8_t* buffer, uintptr_t length); 148 | uint8_t sd_raw_write_interval(offset_t offset, uint8_t* buffer, uintptr_t length, sd_raw_write_interval_handler_t callback, void* p); 149 | uint8_t sd_raw_sync(void); 150 | 151 | #if SD_RAW_WRITE_BUFFERING && !SD_RAW_SAVE_RAM 152 | uint8_t sd_raw_cache_block(offset_t block_address); 153 | #endif 154 | 155 | uint8_t sd_raw_get_info(struct sd_raw_info* info); 156 | 157 | /** 158 | * @} 159 | */ 160 | 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | #endif 166 | 167 | -------------------------------------------------------------------------------- /sd-reader/sd_raw_config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2006-2012 by Roland Riegel 4 | * 5 | * This file is free software; you can redistribute it and/or modify 6 | * it under the terms of either the GNU General Public License version 2 7 | * or the GNU Lesser General Public License version 2.1, both as 8 | * published by the Free Software Foundation. 9 | */ 10 | 11 | #ifndef SD_RAW_CONFIG_H 12 | #define SD_RAW_CONFIG_H 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" 18 | { 19 | #endif 20 | 21 | /** 22 | * \addtogroup sd_raw 23 | * 24 | * @{ 25 | */ 26 | /** 27 | * \file 28 | * MMC/SD support configuration (license: GPLv2 or LGPLv2.1) 29 | */ 30 | 31 | /** 32 | * \ingroup sd_raw_config 33 | * Controls MMC/SD write support. 34 | * 35 | * Set to 1 to enable MMC/SD write support, set to 0 to disable it. 36 | */ 37 | #define SD_RAW_WRITE_SUPPORT 1 38 | 39 | /** 40 | * \ingroup sd_raw_config 41 | * Controls MMC/SD write buffering. 42 | * 43 | * Set to 1 to buffer write accesses, set to 0 to disable it. 44 | * 45 | * \note This option has no effect when SD_RAW_WRITE_SUPPORT is 0. 46 | */ 47 | #define SD_RAW_WRITE_BUFFERING 1 48 | 49 | /** 50 | * \ingroup sd_raw_config 51 | * Controls MMC/SD access buffering. 52 | * 53 | * Set to 1 to save static RAM, but be aware that you will 54 | * lose performance. 55 | * 56 | * \note When SD_RAW_WRITE_SUPPORT is 1, SD_RAW_SAVE_RAM will 57 | * be reset to 0. 58 | */ 59 | #define SD_RAW_SAVE_RAM 0 60 | 61 | /** 62 | * \ingroup sd_raw_config 63 | * Controls support for SDHC cards. 64 | * 65 | * Set to 1 to support so-called SDHC memory cards, i.e. SD 66 | * cards with more than 2 gigabytes of memory. 67 | */ 68 | #define SD_RAW_SDHC 1 69 | 70 | /** 71 | * @} 72 | */ 73 | 74 | /* defines for customisation of sd/mmc port access */ 75 | #if defined(__AVR_ATmega8__) || \ 76 | defined(__AVR_ATmega48__) || \ 77 | defined(__AVR_ATmega48P__) || \ 78 | defined(__AVR_ATmega88__) || \ 79 | defined(__AVR_ATmega88P__) || \ 80 | defined(__AVR_ATmega168__) || \ 81 | defined(__AVR_ATmega168P__) || \ 82 | defined(__AVR_ATmega328P__) 83 | #define configure_pin_mosi() DDRB |= (1 << DDB3) 84 | #define configure_pin_sck() DDRB |= (1 << DDB5) 85 | #define configure_pin_ss() DDRB |= (1 << DDB2) 86 | #define configure_pin_miso() DDRB &= ~(1 << DDB4) 87 | 88 | #define select_card() PORTB &= ~(1 << PORTB2) 89 | #define unselect_card() PORTB |= (1 << PORTB2) 90 | #elif defined(__AVR_ATmega16__) || \ 91 | defined(__AVR_ATmega32__) 92 | #define configure_pin_mosi() DDRB |= (1 << DDB5) 93 | #define configure_pin_sck() DDRB |= (1 << DDB7) 94 | #define configure_pin_ss() DDRB |= (1 << DDB4) 95 | #define configure_pin_miso() DDRB &= ~(1 << DDB6) 96 | 97 | #define select_card() PORTB &= ~(1 << PORTB4) 98 | #define unselect_card() PORTB |= (1 << PORTB4) 99 | #elif defined(__AVR_ATmega64__) || \ 100 | defined(__AVR_ATmega128__) || \ 101 | defined(__AVR_ATmega169__) || \ 102 | defined(__AVR_ATmega32U4__) 103 | #define configure_pin_mosi() DDRB |= (1 << DDB2) 104 | #define configure_pin_sck() DDRB |= (1 << DDB1) 105 | #define configure_pin_ss() DDRB |= (1 << DDB0) 106 | #define configure_pin_miso() DDRB &= ~(1 << DDB3) 107 | 108 | #define select_card() PORTB &= ~(1 << PORTB0) 109 | #define unselect_card() PORTB |= (1 << PORTB0) 110 | #else 111 | #error "no sd/mmc pin mapping available!" 112 | #endif 113 | 114 | #define configure_pin_available() DDRF &= ~(1 << DDF7); PORTF |= (1 << PORTF7); 115 | #define configure_pin_locked() /* Nothing to do here */ 116 | 117 | #define get_pin_available() (PINF & (1 << PINF7)) 118 | #define get_pin_locked() 1 119 | 120 | #if SD_RAW_SDHC 121 | typedef uint64_t offset_t; 122 | #else 123 | typedef uint32_t offset_t; 124 | #endif 125 | 126 | /* configuration checks */ 127 | #if SD_RAW_WRITE_SUPPORT 128 | #undef SD_RAW_SAVE_RAM 129 | #define SD_RAW_SAVE_RAM 0 130 | #else 131 | #undef SD_RAW_WRITE_BUFFERING 132 | #define SD_RAW_WRITE_BUFFERING 0 133 | #endif 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | #endif 140 | 141 | -------------------------------------------------------------------------------- /sd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "sd.h" 7 | 8 | #include "sd-reader/sd_raw.h" 9 | #include "sd-reader/fat.h" 10 | #include "sd-reader/partition.h" 11 | 12 | static struct partition_struct* partition = NULL; 13 | static struct fat_fs_struct* fs = NULL; 14 | static struct fat_dir_struct* root = NULL; 15 | static struct fat_file_struct* file = NULL; 16 | 17 | static unsigned song_count = 0; 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | static inline bool sd_has_extension_mp3(struct fat_dir_entry_struct f) 22 | { 23 | return !strcmp("mp3", f.long_name + strlen(f.long_name) - 3); 24 | } 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | static void sd_count_songs(void) 29 | { 30 | song_count = 0; 31 | 32 | // Search through the memory card to make sure that there's at least 33 | // one file ending in "mp3". 34 | fat_reset_dir(root); 35 | 36 | struct fat_dir_entry_struct file_dir; 37 | while(fat_read_dir(root, &file_dir)) 38 | { 39 | if (sd_has_extension_mp3(file_dir)) song_count++; 40 | } 41 | } 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | 45 | // Initializes the SD card, returning 1 if successful or 0 if failed. 46 | int sd_init(void) 47 | { 48 | return sd_raw_init(); 49 | } 50 | 51 | 52 | void sd_mount_filesystem(void) 53 | { 54 | // Make the function idempotent. 55 | if (partition != NULL && fs != NULL && root != NULL && file != NULL) 56 | return; 57 | 58 | // Make sure that the file system isn't partially mounted. 59 | sd_unmount_filesystem(); 60 | 61 | // Mount the partition 62 | partition = partition_open( 63 | sd_raw_read, sd_raw_read_interval, 64 | sd_raw_write, sd_raw_write_interval, 65 | 0); 66 | 67 | // Mount the filesystem 68 | fs = fat_open(partition); 69 | 70 | // Open the root directory 71 | struct fat_dir_entry_struct root_dir; 72 | fat_get_dir_entry_of_path(fs, "/", &root_dir); 73 | root = fat_open_dir(fs, &root_dir); 74 | 75 | sd_count_songs(); 76 | sd_next_song(); 77 | } 78 | 79 | 80 | void sd_unmount_filesystem(void) 81 | { 82 | if (file != NULL) 83 | fat_close_file(file); 84 | file = NULL; 85 | 86 | if (root != NULL) 87 | fat_close_dir(root); 88 | root = NULL; 89 | 90 | if (fs != NULL) 91 | fat_close(fs); 92 | fs = NULL; 93 | 94 | if (partition != NULL) 95 | partition_close(partition); 96 | partition = NULL; 97 | } 98 | 99 | //////////////////////////////////////////////////////////////////////////////// 100 | 101 | struct fat_dir_entry_struct sd_next_song(void) 102 | { 103 | struct fat_dir_entry_struct file_dir; 104 | file_dir.file_size = 0; 105 | 106 | if (!song_count) return file_dir; 107 | 108 | if (file) fat_close_file(file); 109 | file = NULL; 110 | 111 | while (1) 112 | { 113 | // If we've reached the last file, reset the directory 114 | if (!fat_read_dir(root, &file_dir)) 115 | { 116 | fat_reset_dir(root); 117 | } 118 | 119 | // Check to see if this directory entry has mp3 as its extension 120 | else if (sd_has_extension_mp3(file_dir)) 121 | { 122 | break; 123 | } 124 | } 125 | file = fat_open_file(fs, &file_dir); 126 | 127 | return file_dir; 128 | } 129 | 130 | //////////////////////////////////////////////////////////////////////////////// 131 | 132 | struct fat_dir_entry_struct sd_prev_song(void) 133 | { 134 | struct fat_dir_entry_struct file_dir; 135 | file_dir.file_size = 0; 136 | 137 | if (!song_count) return file_dir; 138 | 139 | for (int i=0; i < song_count - 1; ++i) 140 | { 141 | file_dir = sd_next_song(); 142 | } 143 | return file_dir; 144 | } 145 | 146 | //////////////////////////////////////////////////////////////////////////////// 147 | 148 | bool sd_get_data(uint8_t* buffer, uintptr_t amount) 149 | { 150 | if (file) return fat_read_file(file, buffer, amount) > 0; 151 | else return true; 152 | } 153 | -------------------------------------------------------------------------------- /sd.h: -------------------------------------------------------------------------------- 1 | #ifndef SD_H 2 | #define SD_H 3 | 4 | #include 5 | #include 6 | 7 | #include "sd-reader/fat.h" 8 | 9 | // Initializes the SD filesystem. 10 | // Returns 1 if success, 0 otherwise. 11 | int sd_init(void); 12 | void sd_mount_filesystem(void); 13 | void sd_unmount_filesystem(void); 14 | 15 | struct fat_dir_entry_struct sd_next_song(void); 16 | struct fat_dir_entry_struct sd_prev_song(void); 17 | 18 | bool sd_get_data(uint8_t* buffer, uintptr_t amount); 19 | 20 | /** Checks to see if the SD card is properly initialized. 21 | */ 22 | bool sd_check(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "serial.h" 4 | 5 | #define BAUD 57600 6 | 7 | void serial_init(void) 8 | { 9 | Serial_Init(BAUD, true); 10 | Serial_CreateStream(NULL); 11 | } 12 | -------------------------------------------------------------------------------- /serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H 2 | #define SERIAL_H 3 | 4 | #include 5 | 6 | void serial_init(void); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /tenths.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tenths.h" 5 | 6 | volatile unsigned tenths = 0; 7 | 8 | void tenths_init(void) 9 | { 10 | // Hit output compare about once every tenth of a second 11 | OCR1AH = 0x0c; 12 | OCR1AL = 0x35; 13 | 14 | TCCR1B |= (1 << CS12); // 256x prescalar 15 | TIMSK1 |= (1 << OCIE1A); // interrupt on output compare A 16 | } 17 | 18 | // After each tenth of a second, reset the timer. 19 | ISR(TIMER1_COMPA_vect) 20 | { 21 | tenths++; 22 | TCNT1H = 0; 23 | TCNT1L = 0; 24 | } 25 | -------------------------------------------------------------------------------- /tenths.h: -------------------------------------------------------------------------------- 1 | #ifndef MILLIS_H 2 | #define MILLIS_H 3 | 4 | void tenths_init(void); 5 | extern volatile unsigned tenths; 6 | 7 | #endif 8 | --------------------------------------------------------------------------------