You've already forked dspico-usb-examples
mirror of
https://github.com/LNH-team/dspico-usb-examples.git
synced 2026-01-09 16:27:56 -08:00
Added usb video example
This commit is contained in:
@@ -2,8 +2,9 @@
|
||||
This repository contains examples of using the DSpico USB port from a DS application.
|
||||
|
||||
The `examples` folder contains the following examples:
|
||||
- usb-speaker - Makes the DS/DSi/3DS work as a USB speaker for your PC
|
||||
- mass-storage - Allows access to the DSpico micro SD card over USB
|
||||
- usb-speaker - Makes the DS/DSi/3DS work as a USB speaker for your PC
|
||||
- usb-video - Allows to use the camera of your DSi or 3DS on your PC via USB
|
||||
|
||||
The `platform` folder contains the tiny usb platform code for the DSpico.
|
||||
|
||||
@@ -15,3 +16,7 @@ The examples require a pre-calico devkitpro/libnds environment to build. With su
|
||||
|
||||
## License
|
||||
The platform code and examples are licensed under the Zlib license. For details, see `LICENSE.txt`.
|
||||
|
||||
## Credits
|
||||
- [tinyusb](https://github.com/hathach/tinyusb) for the USB library. The examples are also largely based on the examples that come with tinyusb.
|
||||
- [libcamera](https://github.com/FTC55/libcamera) by [FTC55](https://github.com/FTC55) for camera initialization code
|
||||
|
||||
60
examples/usb-video/Makefile
Normal file
60
examples/usb-video/Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
export TARGET := $(shell basename $(CURDIR))
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
# specify a directory which contains the nitro filesystem
|
||||
# this is relative to the Makefile
|
||||
NITRO_FILES :=
|
||||
|
||||
# These set the information text in the nds file
|
||||
#GAME_TITLE := My Wonderful Homebrew
|
||||
#GAME_SUBTITLE1 := built with devkitARM
|
||||
#GAME_SUBTITLE2 := http://devitpro.org
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
.PHONY: checklibtwl checkarm7 checkarm9 clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: checklibtwl checkarm7 checkarm9 $(TARGET).nds
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checklibtwl:
|
||||
$(MAKE) -C ../../libs/libtwl/libtwl7
|
||||
$(MAKE) -C ../../libs/libtwl/libtwl9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm7: checklibtwl
|
||||
$(MAKE) -C arm7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm9: checklibtwl
|
||||
$(MAKE) -C arm9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
|
||||
ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
|
||||
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \
|
||||
$(_ADDFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
arm7/$(TARGET).elf:
|
||||
$(MAKE) -C arm7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
arm9/$(TARGET).elf:
|
||||
$(MAKE) -C arm9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
$(MAKE) -C arm9 clean
|
||||
$(MAKE) -C arm7 clean
|
||||
rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9
|
||||
134
examples/usb-video/arm7/Makefile
Normal file
134
examples/usb-video/arm7/Makefile
Normal file
@@ -0,0 +1,134 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary files
|
||||
# all directories are relative to this makefile
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := source \
|
||||
../common \
|
||||
../../../platform \
|
||||
../../../libs/tinyusb/src \
|
||||
../../../libs/tinyusb/src/device \
|
||||
../../../libs/tinyusb/src/class/video \
|
||||
../../../libs/tinyusb/src/common
|
||||
INCLUDES := source include build ../common ../../../libs/tinyusb/src ../../../platform
|
||||
DATA :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM7 -DARM7
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
|
||||
-ffunction-sections -fdata-sections\
|
||||
-ffast-math \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
|
||||
-Wsuggest-override -Werror=suggest-override
|
||||
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=../dldi_ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map,--gc-sections -ffunction-sections -fdata-sections
|
||||
|
||||
LIBS := -ltwl7 -lnds7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl7 $(CURDIR)/../../../libs/libtwl/common
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export ARM7ELF := $(CURDIR)/$(TARGET).elf
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) *.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(ARM7ELF) : $(OFILES)
|
||||
@echo linking $(notdir $@)
|
||||
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
232
examples/usb-video/arm7/dldi_ds_arm7.ld
Normal file
232
examples/usb-video/arm7/dldi_ds_arm7.ld
Normal file
@@ -0,0 +1,232 @@
|
||||
/*--------------------------------------------------------------------------------
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
obtain one at https://mozilla.org/MPL/2.0/.
|
||||
--------------------------------------------------------------------------------*/
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
|
||||
|
||||
PHDRS {
|
||||
crt0 PT_LOAD FLAGS(7);
|
||||
arm7 PT_LOAD FLAGS(7);
|
||||
arm7i PT_LOAD FLAGS(0x100007);
|
||||
}
|
||||
|
||||
|
||||
MEMORY {
|
||||
ewram : ORIGIN = 0x02380000, LENGTH = 12M - 512K
|
||||
rom : ORIGIN = 0x08000000, LENGTH = 32M
|
||||
iwram : ORIGIN = 0x037fc000, LENGTH = (96K - 16K)
|
||||
|
||||
twl_ewram : ORIGIN = 0x02e80000, LENGTH = 512K - 64K
|
||||
twl_iwram : ORIGIN = 0x03000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
__iwram_start = ORIGIN(iwram);
|
||||
__iwram_top = ORIGIN(iwram)+ LENGTH(iwram);
|
||||
|
||||
__sp_irq = __iwram_top - 0x100;
|
||||
__sp_svc = __sp_irq - 0x100;
|
||||
__sp_usr = __sp_svc - 0x100;
|
||||
|
||||
__irq_flags = 0x04000000 - 8;
|
||||
__irq_flagsaux = 0x04000000 - 0x40;
|
||||
__irq_vector = 0x04000000 - 4;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
.twl :
|
||||
{
|
||||
__arm7i_lma__ = LOADADDR(.twl);
|
||||
__arm7i_start__ = .;
|
||||
*(.twl)
|
||||
*.twl*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
*.twl*(.rodata)
|
||||
*.twl*(.roda)
|
||||
*.twl*(.rodata.*)
|
||||
*.twl*(.data)
|
||||
*.twl*(.data.*)
|
||||
*.twl*(.gnu.linkonce.d*)
|
||||
. = ALIGN(4);
|
||||
__arm7i_end__ = .;
|
||||
} >twl_iwram AT>twl_ewram :arm7i
|
||||
|
||||
.twl_bss ALIGN(4) (NOLOAD) :
|
||||
{
|
||||
__twl_bss_start__ = .;
|
||||
*(.twl_bss)
|
||||
*.twl.*(.dynbss)
|
||||
*.twl.*(.gnu.linkonce.b*)
|
||||
*.twl.*(.bss*)
|
||||
*.twl.*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__twl_bss_end__ = .;
|
||||
} >twl_iwram :NONE
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >ewram :crt0
|
||||
|
||||
.text :
|
||||
{
|
||||
__arm7_lma__ = LOADADDR(.text);
|
||||
__arm7_start__ = .;
|
||||
KEEP (*(SORT_NONE(.init)))
|
||||
*(.plt)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
KEEP (*(.text.*personality*))
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.glue_7t) *(.glue_7) *(.vfp11_veneer)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram :arm7
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP (*(.fini))
|
||||
} >iwram AT>ewram
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata)
|
||||
*all.rodata*(*)
|
||||
*(.roda)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r*)
|
||||
SORT(CONSTRUCTORS)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >iwram AT>ewram
|
||||
|
||||
.ARM.exidx : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
__exidx_end = .;
|
||||
} >iwram AT>ewram
|
||||
|
||||
/* Ensure the __preinit_array_start label is properly aligned. We
|
||||
could instead move the label definition inside the section, but
|
||||
the linker would then create the section even if it turns out to
|
||||
be empty, which isn't pretty. */
|
||||
.preinit_array : {
|
||||
. = ALIGN(32 / 8);
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.init_array : {
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.fini_array : {
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of the constructors, so
|
||||
we make sure it is first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not actually link against
|
||||
crtbegin.o; the linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it doesn't matter which
|
||||
directory crtbegin.o is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.eh_frame :
|
||||
{
|
||||
KEEP (*(.eh_frame))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.gcc_except_table :
|
||||
{
|
||||
*(.gcc_except_table)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
.jcr : { KEEP (*(.jcr)) } >iwram AT>ewram
|
||||
.got : { *(.got.plt) *(.got) } >iwram AT>ewram
|
||||
|
||||
.data ALIGN(4) : {
|
||||
__data_start = ABSOLUTE(.);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
. = ALIGN(4);
|
||||
__data_end = ABSOLUTE(.) ;
|
||||
} >iwram AT>ewram
|
||||
|
||||
.bss ALIGN(4) (NOLOAD) :
|
||||
{
|
||||
__arm7_end__ = .;
|
||||
__bss_start = ABSOLUTE(.);
|
||||
__bss_start__ = ABSOLUTE(.);
|
||||
*(.dynbss)
|
||||
*(.gnu.linkonce.b*)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
__bss_end__ = ABSOLUTE(.);
|
||||
__end__ = ABSOLUTE(.);
|
||||
} >iwram
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
8
examples/usb-video/arm7/dldi_ds_arm7.specs
Normal file
8
examples/usb-video/arm7/dldi_ds_arm7.specs
Normal file
@@ -0,0 +1,8 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T ../dldi_ds_arm7.ld%s --gc-sections --no-warn-rwx-segments
|
||||
|
||||
*startfile:
|
||||
ds_arm7_crt0%O%s crti%O%s crtbegin%O%s
|
||||
|
||||
8
examples/usb-video/arm7/source/Arm7State.h
Normal file
8
examples/usb-video/arm7/source/Arm7State.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the arm7 state.
|
||||
enum class Arm7State
|
||||
{
|
||||
Idle,
|
||||
ExitRequested
|
||||
};
|
||||
49
examples/usb-video/arm7/source/CameraIpcService.cpp
Normal file
49
examples/usb-video/arm7/source/CameraIpcService.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/i2c/i2c.h>
|
||||
#include "cam_ops.h"
|
||||
#include "CameraIpcService.h"
|
||||
|
||||
#define DEFAULT_COARSE_INTEGRATION_TIME 0x10
|
||||
|
||||
void CameraIpcService::Start()
|
||||
{
|
||||
initParams(DEFAULT_COARSE_INTEGRATION_TIME);
|
||||
ThreadIpcService::Start();
|
||||
}
|
||||
|
||||
void CameraIpcService::HandleMessage(u32 data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case CAMERA_IPC_CMD_INIT_FRONT:
|
||||
{
|
||||
aptinaInit(I2C_DEVICE_CAMERA_FRONT);
|
||||
SendResponseMessage(1);
|
||||
break;
|
||||
}
|
||||
case CAMERA_IPC_CMD_INIT_BACK:
|
||||
{
|
||||
aptinaInit(I2C_DEVICE_CAMERA_BACK);
|
||||
SendResponseMessage(1);
|
||||
break;
|
||||
}
|
||||
case CAMERA_IPC_CMD_ACTIVATE:
|
||||
{
|
||||
aptinaActivate();
|
||||
SendResponseMessage(1);
|
||||
break;
|
||||
}
|
||||
case CAMERA_IPC_CMD_DEACTIVATE:
|
||||
{
|
||||
aptinaDeactivate();
|
||||
SendResponseMessage(1);
|
||||
break;
|
||||
}
|
||||
case CAMERA_IPC_CMD_SWITCH:
|
||||
{
|
||||
aptinaSwitch();
|
||||
SendResponseMessage(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
examples/usb-video/arm7/source/CameraIpcService.h
Normal file
16
examples/usb-video/arm7/source/CameraIpcService.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "ThreadIpcService.h"
|
||||
#include "CameraIpcCommand.h"
|
||||
#include "IpcChannels.h"
|
||||
|
||||
class CameraIpcService : public ThreadIpcService
|
||||
{
|
||||
u32 _threadStack[128];
|
||||
|
||||
public:
|
||||
CameraIpcService()
|
||||
: ThreadIpcService(IPC_CHANNEL_CAMERA, 6, _threadStack, sizeof(_threadStack)) { }
|
||||
|
||||
void Start() override;
|
||||
void HandleMessage(u32 data) override;
|
||||
};
|
||||
10
examples/usb-video/arm7/source/ExitMode.h
Normal file
10
examples/usb-video/arm7/source/ExitMode.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the exit mode of launcher.
|
||||
enum class ExitMode
|
||||
{
|
||||
/// @brief Reset the system (DSi mode only).
|
||||
Reset,
|
||||
/// @brief Power off the system.
|
||||
PowerOff
|
||||
};
|
||||
217
examples/usb-video/arm7/source/cam_ops.cpp
Normal file
217
examples/usb-video/arm7/source/cam_ops.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/i2c/i2c.h>
|
||||
#include <libtwl/i2c/i2cMcu.h>
|
||||
#include "cam_ops.h"
|
||||
|
||||
static u8 sActiveCamera;
|
||||
static u8 sCoarseIntegrationTime;
|
||||
|
||||
static void writeI2C(u8 device, u16 index, u16 data)
|
||||
{
|
||||
rtos_lockMutex(&gI2cMutex);
|
||||
i2c_start(device, I2C_DIRECTION_WRITE);
|
||||
i2c_write(index >> 8, false);
|
||||
i2c_write(index & 0xFF, false);
|
||||
i2c_write(data >> 8, false);
|
||||
i2c_write(data & 0xFF, true);
|
||||
rtos_unlockMutex(&gI2cMutex);
|
||||
}
|
||||
|
||||
static u16 readI2C(u8 device, u16 index)
|
||||
{
|
||||
rtos_lockMutex(&gI2cMutex);
|
||||
i2c_start(device, I2C_DIRECTION_WRITE);
|
||||
i2c_write(index >> 8, false);
|
||||
i2c_write(index & 0xFF, true);
|
||||
i2c_start(device, I2C_DIRECTION_READ);
|
||||
u8 msb = i2c_read(false);
|
||||
u8 lsb = i2c_read(true);
|
||||
rtos_unlockMutex(&gI2cMutex);
|
||||
return (msb << 8) | lsb;
|
||||
}
|
||||
|
||||
static void setApt(u16 index, u16 data)
|
||||
{
|
||||
u16 current = readI2C(sActiveCamera, index);
|
||||
writeI2C(sActiveCamera, index, current | data);
|
||||
}
|
||||
|
||||
static void clrApt(u16 index, u16 data)
|
||||
{
|
||||
u16 current = readI2C(sActiveCamera, index);
|
||||
writeI2C(sActiveCamera, index, current & ~(data));
|
||||
}
|
||||
|
||||
static void waitClrApt(u16 reg, u16 mask)
|
||||
{
|
||||
while (readI2C(sActiveCamera, reg) & mask); //evaluates true whenever a bit is set
|
||||
}
|
||||
|
||||
static void waitSetApt(u16 reg, u16 mask)
|
||||
{
|
||||
while (!(readI2C(sActiveCamera, reg) & mask)); // evaluates true whenever a bit is clear
|
||||
}
|
||||
|
||||
static void writeMCU(u16 index, u16 data)
|
||||
{
|
||||
writeI2C(sActiveCamera, MCU_ADDRESS, index);
|
||||
writeI2C(sActiveCamera, MCU_DATA, data);
|
||||
}
|
||||
|
||||
static u16 readMCU(u16 index)
|
||||
{
|
||||
writeI2C(sActiveCamera, MCU_ADDRESS, index);
|
||||
u16 read = readI2C(sActiveCamera, MCU_DATA);
|
||||
return (read);
|
||||
}
|
||||
|
||||
static void setMCU(u16 index, u16 data)
|
||||
{
|
||||
u16 current = readMCU(index);
|
||||
writeMCU(index, current | data);
|
||||
}
|
||||
|
||||
static void waitClrMCU(u16 reg, u16 mask)
|
||||
{
|
||||
while (readMCU(reg) & mask); //evaluates true whenever a bit is set
|
||||
}
|
||||
|
||||
static void initCamera(u8 device)
|
||||
{
|
||||
sActiveCamera = device;
|
||||
writeI2C(sActiveCamera, 0x001A, 0x0003);
|
||||
writeI2C(sActiveCamera, 0x001A, 0x0000);
|
||||
writeI2C(sActiveCamera, 0x0018, 0x4028);
|
||||
writeI2C(sActiveCamera, 0x001E, 0x0201);
|
||||
writeI2C(sActiveCamera, 0x0016, 0x42DF);
|
||||
waitClrApt(0x0018, 0x4000);
|
||||
waitSetApt(0x301A, 0x0004);
|
||||
writeMCU(0x02F0, 0x0000);
|
||||
writeMCU(0x02F2, 0x0210);
|
||||
writeMCU(0x02F4, 0x001A);
|
||||
writeMCU(0x2145, 0x02F4);
|
||||
writeMCU(0xA134, 0x0001);
|
||||
setMCU(0xA115, 0x0002);
|
||||
writeMCU(0x2755, 0x0002);
|
||||
writeMCU(0x2757, 0x0002);
|
||||
writeI2C(sActiveCamera, 0x0014, 0x2145);
|
||||
writeI2C(sActiveCamera, 0x0010, 0x0111);
|
||||
writeI2C(sActiveCamera, 0x0012, 0x0000);
|
||||
writeI2C(sActiveCamera, 0x0014, 0x244B);
|
||||
writeI2C(sActiveCamera, 0x0014, 0x304B);
|
||||
waitSetApt(0x0014, 0x8000);
|
||||
clrApt(0x0014, 0x0001);
|
||||
writeMCU(0x2703, 0x0100);
|
||||
writeMCU(0x2705, 0x00C0);
|
||||
writeMCU(0x2707, 0x0280);
|
||||
writeMCU(0x2709, 0x01E0);
|
||||
writeMCU(0x2715, 0x0001);
|
||||
writeMCU(0x2719, 0x001A);
|
||||
writeMCU(0x271B, 0x006B);
|
||||
writeMCU(0x271D, 0x006B);
|
||||
writeMCU(0x271F, 0x02C0);
|
||||
writeMCU(0x2721, 0x034B);
|
||||
writeMCU(0xA20B, 0x0000);
|
||||
writeMCU(0xA20C, 0x0006);
|
||||
writeMCU(0x272B, 0x0001);
|
||||
writeMCU(0x272F, 0x001A);
|
||||
writeMCU(0x2731, 0x006B);
|
||||
writeMCU(0x2733, 0x006B);
|
||||
writeMCU(0x2735, 0x02C0);
|
||||
writeMCU(0x2737, 0x034B);
|
||||
setApt(0x3210, 0x0008);
|
||||
writeMCU(0xA208, 0x0000);
|
||||
writeMCU(0xA24C, 0x0020);
|
||||
writeMCU(0xA24F, 0x0070);
|
||||
if (sActiveCamera == I2C_DEVICE_CAMERA_FRONT)
|
||||
{
|
||||
writeMCU(0x2717, 0x0024);
|
||||
writeMCU(0x272D, 0x0024);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMCU(0x2717, 0x0025);
|
||||
writeMCU(0x272D, 0x0025);
|
||||
}
|
||||
if (sActiveCamera == I2C_DEVICE_CAMERA_FRONT)
|
||||
{
|
||||
writeMCU(0xA202, 0x0022);
|
||||
writeMCU(0xA203, 0x00BB);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMCU(0xA202, 0x0000);
|
||||
writeMCU(0xA203, 0x00FF);
|
||||
}
|
||||
setApt(0x0016, 0x0020);
|
||||
writeMCU(0xA115, 0x0072);
|
||||
writeMCU(0xA11F, 0x0001);
|
||||
if (sActiveCamera == I2C_DEVICE_CAMERA_FRONT)
|
||||
{
|
||||
writeI2C(sActiveCamera, 0x326C, 0x0900);
|
||||
writeMCU(0xAB22, 0x0001);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeI2C(sActiveCamera, 0x326C, 0x1000);
|
||||
writeMCU(0xAB22, 0x0002);
|
||||
}
|
||||
writeMCU(0xA103, 0x0006);
|
||||
waitClrMCU(0xA103, 0x000F);
|
||||
writeMCU(0xA103, 0x0005);
|
||||
waitClrMCU(0xA103, 0x000F);
|
||||
|
||||
//for reasons that are way beyond me, if I don't do this then the image gets messed up. RIP all of my color translation code. (mostly same code as aptinaDeactivate)
|
||||
clrApt(0x001A, 0x0200);
|
||||
setApt(0x0018, 0x0001);
|
||||
waitSetApt(0x0018, 0x4000);
|
||||
waitClrApt(0x301A, 0x0004);
|
||||
}
|
||||
|
||||
void aptinaInit(u8 camera) //might want to init both at the same time instead as NO$ suggests, TODO
|
||||
{
|
||||
initCamera(I2C_DEVICE_CAMERA_BACK);
|
||||
initCamera(I2C_DEVICE_CAMERA_FRONT);
|
||||
sActiveCamera = camera; //we set the current camera to the one specified
|
||||
}
|
||||
|
||||
void aptinaActivate()
|
||||
{
|
||||
clrApt(0x0018, 0x0001);
|
||||
waitClrApt(0x0018, 0x4000);
|
||||
waitSetApt(0x301A, 0x0004);
|
||||
writeI2C(sActiveCamera, 0x3012, sCoarseIntegrationTime);
|
||||
setApt(0x001A, 0x0200);
|
||||
|
||||
if (sActiveCamera == I2C_DEVICE_CAMERA_BACK)
|
||||
{
|
||||
mcu_writeReg(MCU_REG_CAMERA, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void aptinaDeactivate()
|
||||
{
|
||||
clrApt(0x001A, 0x0200);
|
||||
setApt(0x0018, 0x0001);
|
||||
waitSetApt(0x0018, 0x4000);
|
||||
waitClrApt(0x301A, 0x0004);
|
||||
|
||||
if (sActiveCamera == I2C_DEVICE_CAMERA_BACK)
|
||||
{
|
||||
mcu_writeReg(MCU_REG_CAMERA, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void aptinaSwitch()
|
||||
{
|
||||
aptinaDeactivate();
|
||||
sActiveCamera = sActiveCamera == I2C_DEVICE_CAMERA_BACK
|
||||
? I2C_DEVICE_CAMERA_FRONT
|
||||
: I2C_DEVICE_CAMERA_BACK;
|
||||
aptinaActivate();
|
||||
}
|
||||
|
||||
void initParams(u8 coarse_int_time)
|
||||
{
|
||||
sCoarseIntegrationTime = coarse_int_time;
|
||||
}
|
||||
13
examples/usb-video/arm7/source/cam_ops.h
Normal file
13
examples/usb-video/arm7/source/cam_ops.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#define MCU_ADDRESS 0x098C
|
||||
#define MCU_DATA 0x0990
|
||||
#define STANDBY_CNT 0x0018
|
||||
|
||||
#define STANDBY_DONE 0x4000
|
||||
|
||||
void aptinaInit(u8 camera);
|
||||
void aptinaActivate();
|
||||
void aptinaDeactivate();
|
||||
void aptinaSwitch();
|
||||
void initParams(u8 coarse_int_time);
|
||||
6
examples/usb-video/arm7/source/common.h
Normal file
6
examples/usb-video/arm7/source/common.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <nds.h>
|
||||
#include <libtwl/rtos/rtosMutex.h>
|
||||
|
||||
extern rtos_mutex_t gCardMutex;
|
||||
extern rtos_mutex_t gI2cMutex;
|
||||
262
examples/usb-video/arm7/source/main.cpp
Normal file
262
examples/usb-video/arm7/source/main.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/sound/soundChannel.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include <libtwl/sound/sound.h>
|
||||
#include <libtwl/ipc/ipcSync.h>
|
||||
#include <libtwl/ipc/ipcFifoSystem.h>
|
||||
#include <libtwl/sys/sysPower.h>
|
||||
#include <libtwl/sio/sioRtc.h>
|
||||
#include <libtwl/sio/sio.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/mem/memSwap.h>
|
||||
#include <libtwl/i2c/i2cMcu.h>
|
||||
#include <libtwl/spi/spiPmic.h>
|
||||
#include "ExitMode.h"
|
||||
#include "Arm7State.h"
|
||||
#include "tusb.h"
|
||||
#include "tusb_config.h"
|
||||
#include "usb_descriptors.h"
|
||||
#include "CameraIpcService.h"
|
||||
|
||||
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT * 2)
|
||||
#define NUMBER_OF_FRAME_BUFFERS 3
|
||||
|
||||
rtos_mutex_t gCardMutex;
|
||||
rtos_mutex_t gI2cMutex;
|
||||
|
||||
static CameraIpcService sCameraIpcService;
|
||||
|
||||
static rtos_thread_t sUsbThread;
|
||||
static u32 sUsbThreadStack[512];
|
||||
|
||||
static rtos_event_t sVBlankEvent;
|
||||
static rtos_event_t sCaptureEvent;
|
||||
static ExitMode sExitMode;
|
||||
static Arm7State sState;
|
||||
static volatile u8 sMcuIrqFlag = false;
|
||||
|
||||
static u32 sCurrentFrame = 0;
|
||||
static vu32 sCapturedFrame = 0xFFFFFFFF;
|
||||
static u8* sFrameBuffer = nullptr;
|
||||
|
||||
static void vblankIrq(u32 irqMask)
|
||||
{
|
||||
rtos_signalEvent(&sVBlankEvent);
|
||||
}
|
||||
|
||||
static void mcuIrq(u32 irq2Mask)
|
||||
{
|
||||
sMcuIrqFlag = true;
|
||||
}
|
||||
|
||||
static void checkMcuIrq(void)
|
||||
{
|
||||
// mcu only exists in DSi mode
|
||||
if (isDSiMode())
|
||||
{
|
||||
// check and ack the flag atomically
|
||||
if (mem_swapByte(false, &sMcuIrqFlag))
|
||||
{
|
||||
// check the irq mask
|
||||
rtos_lockMutex(&gI2cMutex);
|
||||
u32 irqMask = mcu_getIrqMask();
|
||||
rtos_unlockMutex(&gI2cMutex);
|
||||
if (irqMask & MCU_IRQ_RESET)
|
||||
{
|
||||
// power button was released
|
||||
sExitMode = ExitMode::Reset;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
else if (irqMask & MCU_IRQ_POWER_OFF)
|
||||
{
|
||||
// power button was held long to trigger a power off
|
||||
sExitMode = ExitMode::PowerOff;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeVBlankIrq()
|
||||
{
|
||||
rtos_createEvent(&sVBlankEvent);
|
||||
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq);
|
||||
rtos_enableIrqMask(RTOS_IRQ_VBLANK);
|
||||
gfx_setVBlankIrqEnabled(true);
|
||||
}
|
||||
|
||||
static void usbThreadMain(void* arg)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
tud_task();
|
||||
}
|
||||
}
|
||||
|
||||
static void captureIpcMessageHandler(u32 channel, u32 data, void* arg)
|
||||
{
|
||||
if (sFrameBuffer == nullptr)
|
||||
{
|
||||
sFrameBuffer = (u8*)(data << 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
sCapturedFrame++;
|
||||
rtos_signalEvent(&sCaptureEvent);
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeArm7()
|
||||
{
|
||||
rtos_initIrq();
|
||||
rtos_startMainThread();
|
||||
ipc_initFifoSystem();
|
||||
|
||||
// clear sound registers
|
||||
dmaFillWords(0, (void*)0x04000400, 0x100);
|
||||
|
||||
pmic_setAmplifierEnable(true);
|
||||
sys_setSoundPower(true);
|
||||
|
||||
readUserSettings();
|
||||
pmic_setPowerLedBlink(PMIC_CONTROL_POWER_LED_BLINK_NONE);
|
||||
|
||||
sio_setGpioSiIrq(false);
|
||||
sio_setGpioMode(RCNT0_L_MODE_GPIO);
|
||||
|
||||
rtc_init();
|
||||
|
||||
snd_setMasterVolume(127);
|
||||
snd_setMasterEnable(true);
|
||||
|
||||
initializeVBlankIrq();
|
||||
|
||||
if (isDSiMode())
|
||||
{
|
||||
sCameraIpcService.Start();
|
||||
rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq);
|
||||
rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
|
||||
}
|
||||
|
||||
rtos_createEvent(&sCaptureEvent);
|
||||
ipc_setChannelHandler(IPC_CHANNEL_CAPTURE, captureIpcMessageHandler, nullptr);
|
||||
|
||||
ipc_setArm7SyncBits(7);
|
||||
while (ipc_getArm9SyncBits() != 6)
|
||||
{
|
||||
rtos_waitEvent(&sVBlankEvent, true, true);
|
||||
}
|
||||
|
||||
// request two frames in advance
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAPTURE, 0);
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAPTURE, 0);
|
||||
|
||||
tusb_rhport_init_t dev_init =
|
||||
{
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO
|
||||
};
|
||||
tusb_init(0, &dev_init);
|
||||
|
||||
rtos_createThread(&sUsbThread, 8, usbThreadMain, NULL, sUsbThreadStack, sizeof(sUsbThreadStack));
|
||||
rtos_wakeupThread(&sUsbThread);
|
||||
}
|
||||
|
||||
static void updateArm7IdleState()
|
||||
{
|
||||
checkMcuIrq();
|
||||
|
||||
if (sState == Arm7State::ExitRequested)
|
||||
{
|
||||
snd_setMasterVolume(0); // mute sound
|
||||
}
|
||||
}
|
||||
|
||||
static bool performExit(ExitMode exitMode)
|
||||
{
|
||||
switch (exitMode)
|
||||
{
|
||||
case ExitMode::Reset:
|
||||
{
|
||||
rtos_lockMutex(&gI2cMutex);
|
||||
mcu_setWarmBootFlag(true);
|
||||
mcu_hardReset();
|
||||
rtos_unlockMutex(&gI2cMutex);
|
||||
break;
|
||||
}
|
||||
case ExitMode::PowerOff:
|
||||
{
|
||||
pmic_shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (true); // wait infinitely for exit
|
||||
}
|
||||
|
||||
static void updateArm7ExitRequestedState()
|
||||
{
|
||||
performExit(sExitMode);
|
||||
}
|
||||
|
||||
static void updateArm7()
|
||||
{
|
||||
switch (sState)
|
||||
{
|
||||
case Arm7State::Idle:
|
||||
{
|
||||
updateArm7IdleState();
|
||||
break;
|
||||
}
|
||||
case Arm7State::ExitRequested:
|
||||
{
|
||||
updateArm7ExitRequestedState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tud_video_frame_xfer_complete_cb(u8 ctl_idx, u8 stm_idx)
|
||||
{
|
||||
(void) ctl_idx;
|
||||
(void) stm_idx;
|
||||
}
|
||||
|
||||
int tud_video_commit_cb(u8 ctl_idx, u8 stm_idx, const video_probe_and_commit_control_t* parameters)
|
||||
{
|
||||
(void) ctl_idx;
|
||||
(void) stm_idx;
|
||||
return VIDEO_ERROR_NONE;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
sState = Arm7State::Idle;
|
||||
initializeArm7();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (tud_video_n_streaming(0, 0))
|
||||
{
|
||||
rtos_waitEvent(&sCaptureEvent, false, true);
|
||||
while (sCurrentFrame != (sCapturedFrame + 1))
|
||||
{
|
||||
updateArm7();
|
||||
tud_video_n_frame_xfer(0, 0,
|
||||
sFrameBuffer + (sCurrentFrame % NUMBER_OF_FRAME_BUFFERS) * FRAME_SIZE, FRAME_SIZE);
|
||||
sCurrentFrame++;
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAPTURE, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rtos_waitEvent(&sVBlankEvent, true, true);
|
||||
updateArm7();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
84
examples/usb-video/arm7/source/tusb_config.h
Normal file
84
examples/usb-video/arm7/source/tusb_config.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board Specific Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_TUD_RHPORT
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
#ifndef BOARD_TUD_MAX_SPEED
|
||||
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by compiler flags for flexibility
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#define CFG_TUSB_MCU OPT_MCU_DSPICO
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_CUSTOM
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Enable Device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// Default is max speed that hardware controller could support with on-chip PHY
|
||||
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_VIDEO 1
|
||||
|
||||
// The number of video streaming interfaces
|
||||
#define CFG_TUD_VIDEO_STREAMING 1
|
||||
|
||||
// video streaming endpoint buffer size
|
||||
#define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE 512
|
||||
|
||||
// use bulk endpoint for streaming interface
|
||||
#define CFG_TUD_VIDEO_STREAMING_BULK 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
337
examples/usb-video/arm7/source/usb_descriptors.cpp
Normal file
337
examples/usb-video/arm7/source/usb_descriptors.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#include "common.h"
|
||||
#include "UsbStringDescriptor.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] VIDEO | AUDIO | MIDI | HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) )
|
||||
|
||||
#define USB_VID 0xCafe
|
||||
#define USB_BCD 0x0200
|
||||
|
||||
enum
|
||||
{
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
STRID_UVC_CONTROL,
|
||||
STRID_UVC_STREAMING
|
||||
};
|
||||
|
||||
const u8* tud_descriptor_device_cb(void)
|
||||
{
|
||||
static const tusb_desc_device_t deviceDescriptor =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for Video
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = USB_VID,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = STRID_MANUFACTURER,
|
||||
.iProduct = STRID_PRODUCT,
|
||||
.iSerialNumber = STRID_SERIAL,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
return (const u8*)&deviceDescriptor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
/* Time stamp base clock. It is a deprecated parameter. */
|
||||
#define UVC_CLOCK_FREQUENCY 27000000
|
||||
|
||||
/* video capture path */
|
||||
#define UVC_ENTITY_CAP_INPUT_TERMINAL 0x01
|
||||
#define UVC_ENTITY_CAP_PROCESSING 0x02
|
||||
#define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x03
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_VIDEO_CONTROL,
|
||||
ITF_NUM_VIDEO_STREAMING,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
#define EPNUM_VIDEO_IN 0x81
|
||||
|
||||
#define USE_ISO_STREAMING (!CFG_TUD_VIDEO_STREAMING_BULK)
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
tusb_desc_interface_t itf;
|
||||
tusb_desc_video_control_header_1itf_t header;
|
||||
tusb_desc_video_control_camera_terminal_t camera_terminal;
|
||||
u8 processing_unit[13];
|
||||
tusb_desc_video_control_output_terminal_t output_terminal;
|
||||
} uvc_control_desc_t;
|
||||
|
||||
/* Windows support YUY2 and NV12
|
||||
* https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
tusb_desc_interface_t itf;
|
||||
tusb_desc_video_streaming_input_header_1byte_t header;
|
||||
tusb_desc_video_format_uncompressed_t format;
|
||||
tusb_desc_video_frame_uncompressed_2int_t frame;
|
||||
tusb_desc_video_streaming_color_matching_t color;
|
||||
|
||||
#if USE_ISO_STREAMING
|
||||
// For ISO streaming, USB spec requires to alternate interface
|
||||
tusb_desc_interface_t itf_alt;
|
||||
#endif
|
||||
|
||||
tusb_desc_endpoint_t ep;
|
||||
} uvc_streaming_desc_t;
|
||||
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
tusb_desc_configuration_t config;
|
||||
tusb_desc_interface_assoc_t iad;
|
||||
uvc_control_desc_t video_control;
|
||||
uvc_streaming_desc_t video_streaming;
|
||||
} uvc_cfg_desc_t;
|
||||
|
||||
const u8* tud_descriptor_configuration_cb(u8 index)
|
||||
{
|
||||
static const uvc_cfg_desc_t configurationDescriptor =
|
||||
{
|
||||
.config = {
|
||||
.bLength = sizeof(tusb_desc_configuration_t),
|
||||
.bDescriptorType = TUSB_DESC_CONFIGURATION,
|
||||
|
||||
.wTotalLength = sizeof(uvc_cfg_desc_t),
|
||||
.bNumInterfaces = ITF_NUM_TOTAL,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = 0,
|
||||
.bmAttributes = TU_BIT(7),
|
||||
.bMaxPower = 100 / 2
|
||||
},
|
||||
.iad = {
|
||||
.bLength = sizeof(tusb_desc_interface_assoc_t),
|
||||
.bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
|
||||
|
||||
.bFirstInterface = ITF_NUM_VIDEO_CONTROL,
|
||||
.bInterfaceCount = 2,
|
||||
.bFunctionClass = TUSB_CLASS_VIDEO,
|
||||
.bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION,
|
||||
.bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
|
||||
.iFunction = 0
|
||||
},
|
||||
|
||||
.video_control = {
|
||||
.itf = {
|
||||
.bLength = sizeof(tusb_desc_interface_t),
|
||||
.bDescriptorType = TUSB_DESC_INTERFACE,
|
||||
|
||||
.bInterfaceNumber = ITF_NUM_VIDEO_CONTROL,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = TUSB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL,
|
||||
.bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
|
||||
.iInterface = STRID_UVC_CONTROL
|
||||
},
|
||||
.header = {
|
||||
.bLength = sizeof(tusb_desc_video_control_header_1itf_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER,
|
||||
|
||||
.bcdUVC = VIDEO_BCD_1_50,
|
||||
.wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t)/* - sizeof(tusb_desc_endpoint_t) - 5*/, // CS VC descriptors only
|
||||
.dwClockFrequency = UVC_CLOCK_FREQUENCY,
|
||||
.bInCollection = 1,
|
||||
.baInterfaceNr = { ITF_NUM_VIDEO_STREAMING }
|
||||
},
|
||||
.camera_terminal = {
|
||||
.bLength = sizeof(tusb_desc_video_control_camera_terminal_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL,
|
||||
|
||||
.bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL,
|
||||
.wTerminalType = VIDEO_ITT_CAMERA,
|
||||
.bAssocTerminal = 0,
|
||||
.iTerminal = 0,
|
||||
.wObjectiveFocalLengthMin = 0,
|
||||
.wObjectiveFocalLengthMax = 0,
|
||||
.wOcularFocalLength = 0,
|
||||
.bControlSize = 3,
|
||||
.bmControls = { 0, 0, 0 }
|
||||
},
|
||||
.processing_unit = { 0x0D, 0x24, 0x05, UVC_ENTITY_CAP_PROCESSING, UVC_ENTITY_CAP_INPUT_TERMINAL, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
.output_terminal = {
|
||||
.bLength = sizeof(tusb_desc_video_control_output_terminal_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL,
|
||||
|
||||
.bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
|
||||
.wTerminalType = VIDEO_TT_STREAMING,
|
||||
.bAssocTerminal = 0,
|
||||
.bSourceID = UVC_ENTITY_CAP_PROCESSING,
|
||||
.iTerminal = 0
|
||||
}
|
||||
},
|
||||
|
||||
.video_streaming = {
|
||||
.itf = {
|
||||
.bLength = sizeof(tusb_desc_interface_t),
|
||||
.bDescriptorType = TUSB_DESC_INTERFACE,
|
||||
|
||||
.bInterfaceNumber = ITF_NUM_VIDEO_STREAMING,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = CFG_TUD_VIDEO_STREAMING_BULK, // bulk 1, iso 0
|
||||
.bInterfaceClass = TUSB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING,
|
||||
.bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
|
||||
.iInterface = STRID_UVC_STREAMING
|
||||
},
|
||||
.header = {
|
||||
.bLength = sizeof(tusb_desc_video_streaming_input_header_1byte_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER,
|
||||
|
||||
.bNumFormats = 1,
|
||||
.wTotalLength = sizeof(uvc_streaming_desc_t) - sizeof(tusb_desc_interface_t)
|
||||
- sizeof(tusb_desc_endpoint_t) - (USE_ISO_STREAMING ? sizeof(tusb_desc_interface_t) : 0) , // CS VS descriptors only
|
||||
.bEndpointAddress = EPNUM_VIDEO_IN,
|
||||
.bmInfo = 0,
|
||||
.bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
|
||||
.bStillCaptureMethod = 0,
|
||||
.bTriggerSupport = 0,
|
||||
.bTriggerUsage = 0,
|
||||
.bControlSize = 1,
|
||||
.bmaControls = { 0 }
|
||||
},
|
||||
.format = {
|
||||
.bLength = sizeof(tusb_desc_video_format_uncompressed_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED,
|
||||
.bFormatIndex = 1, // 1-based index
|
||||
.bNumFrameDescriptors = 1,
|
||||
.guidFormat = { TUD_VIDEO_GUID_YUY2 },
|
||||
.bBitsPerPixel = 16,
|
||||
.bDefaultFrameIndex = 1,
|
||||
.bAspectRatioX = 0,
|
||||
.bAspectRatioY = 0,
|
||||
.bmInterlaceFlags = 0,
|
||||
.bCopyProtect = 0
|
||||
},
|
||||
.frame = {
|
||||
.bLength = sizeof(tusb_desc_video_frame_uncompressed_2int_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED,
|
||||
.bFrameIndex = 1, // 1-based index
|
||||
.bmCapabilities = 0,
|
||||
.wWidth = FRAME_WIDTH,
|
||||
.wHeight = FRAME_HEIGHT,
|
||||
.dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1,
|
||||
.dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE,
|
||||
.dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8,
|
||||
.dwDefaultFrameInterval = 10000000 / FRAME_RATE,
|
||||
.bFrameIntervalType = 2, // 2 discrete
|
||||
.dwFrameInterval = {
|
||||
10000000 / FRAME_RATE,
|
||||
10000000
|
||||
}
|
||||
},
|
||||
.color = {
|
||||
.bLength = sizeof(tusb_desc_video_streaming_color_matching_t),
|
||||
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
|
||||
.bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT,
|
||||
|
||||
.bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709,
|
||||
.bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709,
|
||||
.bMatrixCoefficients = VIDEO_COLOR_COEF_BT709
|
||||
},
|
||||
|
||||
#if USE_ISO_STREAMING
|
||||
.itf_alt = {
|
||||
.bLength = sizeof(tusb_desc_interface_t),
|
||||
.bDescriptorType = TUSB_DESC_INTERFACE,
|
||||
|
||||
.bInterfaceNumber = ITF_NUM_VIDEO_STREAMING,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = TUSB_CLASS_VIDEO,
|
||||
.bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING,
|
||||
.bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
|
||||
.iInterface = STRID_UVC_STREAMING
|
||||
},
|
||||
#endif
|
||||
|
||||
.ep = {
|
||||
.bLength = sizeof(tusb_desc_endpoint_t),
|
||||
.bDescriptorType = TUSB_DESC_ENDPOINT,
|
||||
|
||||
.bEndpointAddress = EPNUM_VIDEO_IN,
|
||||
.bmAttributes = {
|
||||
.xfer = CFG_TUD_VIDEO_STREAMING_BULK ? TUSB_XFER_BULK : TUSB_XFER_ISOCHRONOUS,
|
||||
.sync = CFG_TUD_VIDEO_STREAMING_BULK ? 0 : 1 // asynchronous
|
||||
},
|
||||
.wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE,
|
||||
.bInterval = 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (const u8*)&configurationDescriptor;
|
||||
}
|
||||
|
||||
const u16* tud_descriptor_string_cb(u8 index, u16 langid)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case STRID_LANGID:
|
||||
{
|
||||
static const UsbStringDescriptor<2> descriptor(0x0409);
|
||||
return (const u16*)&descriptor;
|
||||
}
|
||||
case STRID_MANUFACTURER:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"LNH");
|
||||
}
|
||||
case STRID_PRODUCT:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"DSpico Camera Example");
|
||||
}
|
||||
case STRID_SERIAL:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"123456789");
|
||||
}
|
||||
case STRID_UVC_CONTROL:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"UVC Control");
|
||||
}
|
||||
case STRID_UVC_STREAMING:
|
||||
{
|
||||
return USB_STRING_DESCRIPTOR(u"UVC Streaming");
|
||||
}
|
||||
default:
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
5
examples/usb-video/arm7/source/usb_descriptors.h
Normal file
5
examples/usb-video/arm7/source/usb_descriptors.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define FRAME_WIDTH 256
|
||||
#define FRAME_HEIGHT 192
|
||||
#define FRAME_RATE 10 // just an indication, we won't actually reach it
|
||||
153
examples/usb-video/arm9/Makefile
Normal file
153
examples/usb-video/arm9/Makefile
Normal file
@@ -0,0 +1,153 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary files
|
||||
# all directories are relative to this makefile
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := source \
|
||||
../common \
|
||||
source/core
|
||||
INCLUDES := include source ../common
|
||||
DATA := data
|
||||
GRAPHICS := gfx
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM9 -DARM9
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
|
||||
-ffunction-sections -fdata-sections\
|
||||
-ffast-math \
|
||||
-fno-devirtualize-speculatively \
|
||||
-Werror=return-type \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
|
||||
-Wsuggest-override -Werror=suggest-override
|
||||
|
||||
CFLAGS += -Werror=implicit-function-declaration
|
||||
|
||||
ASFLAGS := -g $(ARCH) $(INCLUDE) -march=armv5te -mtune=arm946e-s
|
||||
|
||||
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--gc-sections,--use-blx -ffunction-sections -fdata-sections
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -ltwl9 -lnds9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl9 $(CURDIR)/../../../libs/libtwl/common
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export ARM9ELF := $(CURDIR)/$(TARGET).elf
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PNGFILES:.png=.o)\
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) *.elf *.nds* *.bin
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(ARM9ELF) : $(OFILES)
|
||||
@echo linking $(notdir $@)
|
||||
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.nft2.o : %.nft2
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule creates assembly source files using grit
|
||||
# grit takes an image file and a .grit describing how the file is to be processed
|
||||
# add additional rules like this for each image extension
|
||||
# you use in the graphics folders
|
||||
#---------------------------------------------------------------------------------
|
||||
%.s %.h: %.png %.grit
|
||||
#---------------------------------------------------------------------------------
|
||||
grit $< -fts -o$*
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
84
examples/usb-video/arm9/source/Camera.cpp
Normal file
84
examples/usb-video/arm9/source/Camera.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "common.h"
|
||||
#include <libtwl/sys/swi.h>
|
||||
#include <libtwl/dma/dmaTwl.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/ipc/ipcFifoSystem.h>
|
||||
#include "IpcChannels.h"
|
||||
#include "CameraIpcCommand.h"
|
||||
#include "Camera.h"
|
||||
|
||||
#define REG_SCFG_CLK (*(vu32*)0x04004004)
|
||||
#define REG_CAM_MCNT (*(vu16*)0x04004200)
|
||||
#define REG_CAM_CNT (*(vu16*)0x04004202)
|
||||
#define REG_CAM_DAT (*(vu32*)0x04004204)
|
||||
|
||||
static rtos_event_t sEvent;
|
||||
|
||||
static void ipcMessageHandler(u32 channel, u32 data, void* arg)
|
||||
{
|
||||
rtos_signalEvent(&sEvent);
|
||||
}
|
||||
|
||||
void cam_init(bool front)
|
||||
{
|
||||
rtos_createEvent(&sEvent);
|
||||
ipc_setChannelHandler(IPC_CHANNEL_CAMERA, ipcMessageHandler, nullptr);
|
||||
|
||||
REG_SCFG_CLK |= 0x0004;
|
||||
REG_CAM_MCNT = 0x0000;
|
||||
swi_waitByLoop(0x1E);
|
||||
REG_SCFG_CLK |= 0x0100;
|
||||
swi_waitByLoop(0x1E);
|
||||
REG_CAM_MCNT = 0x0022;
|
||||
swi_waitByLoop(0x2008);
|
||||
REG_SCFG_CLK &= ~0x0100;
|
||||
REG_CAM_CNT &= ~0x8000;
|
||||
REG_CAM_CNT |= 0x0020;
|
||||
REG_CAM_CNT = (REG_CAM_CNT & ~0x0300) | 0x0200;
|
||||
REG_CAM_CNT |= 0x0400;
|
||||
REG_CAM_CNT |= 0x0800;
|
||||
REG_SCFG_CLK |= 0x0100;
|
||||
swi_waitByLoop(0x14);
|
||||
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAMERA, front ? CAMERA_IPC_CMD_INIT_FRONT : CAMERA_IPC_CMD_INIT_BACK);
|
||||
rtos_waitEvent(&sEvent, false, true);
|
||||
|
||||
REG_SCFG_CLK &= ~0x0100;
|
||||
REG_SCFG_CLK |= 0x0100;
|
||||
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAMERA, CAMERA_IPC_CMD_ACTIVATE);
|
||||
rtos_waitEvent(&sEvent, false, true);
|
||||
}
|
||||
|
||||
void cam_stop()
|
||||
{
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAMERA, CAMERA_IPC_CMD_DEACTIVATE);
|
||||
rtos_waitEvent(&sEvent, false, true);
|
||||
}
|
||||
|
||||
void cam_switch()
|
||||
{
|
||||
ipc_sendFifoMessage(IPC_CHANNEL_CAMERA, CAMERA_IPC_CMD_SWITCH);
|
||||
rtos_waitEvent(&sEvent, false, true);
|
||||
}
|
||||
|
||||
void cam_dmaStart(int dma, void* dst)
|
||||
{
|
||||
// REG_CAM_CNT |= 0x2000;
|
||||
REG_CAM_CNT &= ~0x2000;
|
||||
REG_CAM_CNT = (REG_CAM_CNT & ~0x000F) | 0x0003;
|
||||
REG_CAM_CNT |= 0x0020;
|
||||
REG_CAM_CNT |= 0x8000;
|
||||
|
||||
dma_twl_config_t dmaConfig =
|
||||
{
|
||||
.src = (const void*)0x04004204,
|
||||
.dst = dst,
|
||||
.totalWordCount = 0x6000,
|
||||
.wordCount = 0x200,
|
||||
.blockInterval = 2,
|
||||
.fillData = 0,
|
||||
.control = 0x8B044000
|
||||
};
|
||||
dma_twlSetParams(dma, &dmaConfig);
|
||||
}
|
||||
6
examples/usb-video/arm9/source/Camera.h
Normal file
6
examples/usb-video/arm9/source/Camera.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
void cam_init(bool front);
|
||||
void cam_stop();
|
||||
void cam_switch();
|
||||
void cam_dmaStart(int dma, void* dst);
|
||||
2
examples/usb-video/arm9/source/common.h
Normal file
2
examples/usb-video/arm9/source/common.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include <nds/ndstypes.h>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user