Merge remote-tracking branch 'origin/Develop'

Develop
Sven Steudte 2017-09-07 02:11:28 +02:00
commit 8f873fe309
55 zmienionych plików z 2696 dodań i 10585 usunięć

Wyświetl plik

@ -108,7 +108,11 @@ def received_data(data):
print 'Send to SSDV data server: OK'
error = False
except urllib2.URLError, error:
print 'Send to SSDV data server: failed (connection error :( trying again...)'
if error.code == 400:
print 'The SSDV server indicated a faulty packet: ' + error.read()
error = False
else:
print 'Send to SSDV data server: failed (connection error :( trying again...)'
except urllib2.HTTPError, error: # The server did not like our packets :(
print 'Send to SSDV data server: failed (the server did not like our packets :( )'

Wyświetl plik

@ -2715,9 +2715,7 @@ Wire Wire Line
Wire Wire Line
22100 13500 22200 13500
Wire Wire Line
22100 13600 22200 13600
Wire Wire Line
22200 13600 22600 13600
22100 13600 22600 13600
Wire Wire Line
21000 13200 21000 13500
Wire Wire Line
@ -2762,9 +2760,7 @@ Wire Wire Line
Wire Wire Line
18000 11800 18000 11600
Wire Wire Line
17900 11800 18000 11800
Wire Wire Line
18000 11800 18100 11800
17900 11800 18100 11800
Wire Wire Line
17500 11800 17600 11800
Wire Wire Line
@ -2793,17 +2789,13 @@ Wire Wire Line
Wire Wire Line
5000 8350 5000 9450
Wire Wire Line
5000 8350 5300 8350
Wire Wire Line
5300 8350 5400 8350
5000 8350 5400 8350
Wire Wire Line
4900 9350 4800 9350
Wire Wire Line
4900 8850 4900 9350
Wire Wire Line
4900 8850 5300 8850
Wire Wire Line
5300 8850 5400 8850
4900 8850 5400 8850
Connection ~ 3800 9150
Wire Wire Line
3800 9250 3900 9250
@ -2812,47 +2804,19 @@ Wire Wire Line
Wire Wire Line
3800 9550 3900 9550
Wire Wire Line
1000 9150 1700 9150
Wire Wire Line
1700 9150 2100 9150
Wire Wire Line
2100 9150 2650 9150
Wire Wire Line
2650 9150 3100 9150
Wire Wire Line
3100 9150 3800 9150
Wire Wire Line
3800 9150 3900 9150
1000 9150 3900 9150
Connection ~ 5100 9650
Wire Wire Line
4800 9150 5400 9150
4800 9150 7600 9150
Wire Wire Line
5400 9150 5700 9150
18400 11800 19100 11800
Wire Wire Line
5700 9150 6500 9150
Wire Wire Line
6500 9150 7250 9150
Wire Wire Line
7250 9150 7500 9150
Wire Wire Line
7500 9150 7600 9150
Wire Wire Line
18400 11800 19000 11800
Wire Wire Line
19000 11800 19100 11800
Wire Wire Line
7800 14600 7800 14700
Wire Wire Line
7800 14700 7800 14900
Wire Wire Line
7800 14900 7800 15000
7800 14600 7800 15000
Wire Wire Line
7500 9150 7500 9250
Connection ~ 6500 8350
Wire Wire Line
6500 8250 6500 8350
Wire Wire Line
6500 8350 6500 8450
6500 8250 6500 8450
Wire Wire Line
5700 8350 6500 8350
Wire Wire Line
@ -2869,9 +2833,7 @@ Connection ~ 1700 9150
Wire Wire Line
1700 9250 1700 9150
Wire Wire Line
1000 9950 1700 9950
Wire Wire Line
1700 9950 1800 9950
1000 9950 1800 9950
Connection ~ 2100 9600
Wire Wire Line
20150 7350 20050 7350
@ -2899,9 +2861,7 @@ Wire Wire Line
Wire Wire Line
7350 11450 7450 11450
Wire Wire Line
7350 11350 7350 11450
Wire Wire Line
7350 11450 7350 11550
7350 11350 7350 11550
Connection ~ 2100 9150
Wire Wire Line
1000 9600 2100 9600
@ -2917,15 +2877,9 @@ Connection ~ 6500 8850
Wire Wire Line
5700 8850 6500 8850
Wire Wire Line
6500 8750 6500 8850
6500 8750 6500 9250
Wire Wire Line
6500 8850 6500 9150
Wire Wire Line
6500 9150 6500 9250
Wire Wire Line
2100 9550 2100 9600
Wire Wire Line
2100 9600 2100 9700
2100 9550 2100 9700
Wire Wire Line
2100 9150 2100 9250
Wire Wire Line
@ -2998,36 +2952,22 @@ Wire Wire Line
Wire Wire Line
13300 7000 13300 6900
Wire Wire Line
13050 6300 13300 6300
Wire Wire Line
13300 6300 13500 6300
13050 6300 13500 6300
Wire Wire Line
20650 7250 20650 7350
Wire Wire Line
8300 9150 8400 9150
Wire Wire Line
9000 9150 9100 9150
Wire Wire Line
9100 9150 9350 9150
Wire Wire Line
9350 9150 9700 9150
Wire Wire Line
9700 9150 9900 9150
9000 9150 9900 9150
Wire Wire Line
9100 9150 9100 9200
Wire Wire Line
9350 9150 9350 9200
Connection ~ 9100 9150
Wire Wire Line
9100 9500 9100 9550
9100 9500 9100 9600
Wire Wire Line
9100 9550 9100 9600
Wire Wire Line
8300 9550 8900 9550
Wire Wire Line
8900 9550 9100 9550
Wire Wire Line
9100 9550 9350 9550
8300 9550 9350 9550
Wire Wire Line
9350 9550 9350 9500
Connection ~ 9100 9550
@ -3042,11 +2982,7 @@ Connection ~ 9700 9150
Wire Wire Line
7600 9350 7500 9350
Wire Wire Line
7500 9350 7500 9450
Wire Wire Line
7500 9450 7500 9550
Wire Wire Line
7500 9550 7500 10000
7500 9350 7500 10000
Wire Wire Line
7500 9450 7600 9450
Wire Wire Line
@ -3056,23 +2992,17 @@ Connection ~ 7500 9550
Wire Wire Line
7500 9250 7600 9250
Wire Wire Line
7250 9050 7250 9150
Wire Wire Line
7250 9150 7250 9450
7250 9050 7250 9450
Connection ~ 7500 9150
Wire Wire Line
7250 9750 7250 10000
Wire Wire Line
2650 9550 2650 9650
Wire Wire Line
2650 9650 2650 9750
2650 9550 2650 9750
Wire Wire Line
2650 10050 2650 10150
Connection ~ 7250 9150
Wire Wire Line
6500 9550 6500 9650
Wire Wire Line
6500 9650 6500 9750
6500 9550 6500 9750
Wire Wire Line
6500 10050 6500 10150
Wire Wire Line
@ -3089,13 +3019,9 @@ Connection ~ 6500 9150
Wire Wire Line
5100 9650 5100 9750
Wire Wire Line
4800 9650 5100 9650
4800 9650 5400 9650
Wire Wire Line
5100 9650 5400 9650
Wire Wire Line
5400 9550 5400 9650
Wire Wire Line
5400 9650 5400 9750
5400 9550 5400 9750
Connection ~ 5400 9650
Wire Wire Line
5400 9150 5400 9250
@ -3112,24 +3038,16 @@ Wire Wire Line
Wire Wire Line
3100 9550 3200 9550
Wire Wire Line
3100 9050 3100 9150
Wire Wire Line
3100 9150 3100 9550
Wire Wire Line
3100 9550 3100 9700
3100 9050 3100 9700
Wire Wire Line
3100 10150 3100 10000
Connection ~ 3100 9550
Connection ~ 3100 9150
Wire Wire Line
6500 7050 6950 7050
Wire Wire Line
6950 7050 7100 7050
6500 7050 7100 7050
Connection ~ 6950 7050
Wire Wire Line
6750 7150 6950 7150
Wire Wire Line
6950 7150 7100 7150
6750 7150 7100 7150
Connection ~ 6950 7150
Wire Wire Line
6750 7250 6750 7150
@ -3144,9 +3062,7 @@ Wire Wire Line
Wire Wire Line
7050 6700 7050 6800
Wire Wire Line
6250 6800 7050 6800
Wire Wire Line
7050 6800 7100 6800
6250 6800 7100 6800
Wire Wire Line
6250 7250 6250 6800
Connection ~ 7050 6800
@ -3155,24 +3071,16 @@ Wire Wire Line
Wire Wire Line
8100 7050 8200 7050
Wire Wire Line
8200 7050 8200 7150
Wire Wire Line
8200 7150 8200 7650
8200 7050 8200 7650
Wire Wire Line
8200 7150 8100 7150
Connection ~ 8200 7150
Wire Wire Line
8400 6500 8400 6600
8400 6500 8400 7000
Wire Wire Line
8400 6600 8400 7000
8650 6500 8650 7000
Wire Wire Line
8650 6500 8650 6700
Wire Wire Line
8650 6700 8650 7000
Wire Wire Line
8900 6500 8900 6800
Wire Wire Line
8900 6800 8900 7000
8900 6500 8900 7000
Connection ~ 8400 6600
Connection ~ 8650 6700
Connection ~ 8900 6800
@ -3185,16 +3093,12 @@ Wire Wire Line
Wire Wire Line
12800 7000 12800 6900
Wire Wire Line
12800 6400 12800 6500
Wire Wire Line
12800 6500 12800 6600
12800 6400 12800 6600
Connection ~ 12800 6500
Wire Wire Line
12800 6100 12800 5800
Wire Wire Line
4150 14600 4150 14700
Wire Wire Line
4150 14700 4150 15600
4150 14600 4150 15600
Wire Wire Line
4150 14700 4050 14700
Wire Wire Line
@ -3211,19 +3115,9 @@ Wire Wire Line
Wire Wire Line
2350 14600 3050 14600
Wire Wire Line
2800 13500 2950 13500
2800 13500 3050 13500
Wire Wire Line
2950 13500 3050 13500
Wire Wire Line
2950 12450 2950 12550
Wire Wire Line
2950 12550 2950 12800
Wire Wire Line
2950 12800 2950 13050
Wire Wire Line
2950 13050 2950 13400
Wire Wire Line
2950 13400 2950 13500
2950 12450 2950 13500
Wire Wire Line
2950 13400 3050 13400
Connection ~ 2950 13400
@ -3243,11 +3137,7 @@ Wire Wire Line
2950 12800 2850 12800
Connection ~ 2950 12800
Wire Wire Line
2450 12550 2450 12800
Wire Wire Line
2450 12800 2450 13050
Wire Wire Line
2450 13050 2450 13150
2450 12550 2450 13150
Wire Wire Line
2450 13050 2550 13050
Wire Wire Line
@ -3264,30 +3154,22 @@ Wire Wire Line
Wire Wire Line
4150 12900 4150 13000
Wire Wire Line
4050 14300 4400 14300
Wire Wire Line
4400 14300 4500 14300
4050 14300 4500 14300
Wire Wire Line
4400 14300 4400 14400
Connection ~ 4400 14300
Wire Wire Line
4400 15000 4400 15100
Wire Wire Line
4400 15100 4400 15200
4400 15000 4400 15200
Wire Wire Line
4400 15100 4550 15100
Connection ~ 4400 15100
Wire Wire Line
4400 15600 4400 15500
Wire Wire Line
13300 5800 13300 6300
Wire Wire Line
13300 6300 13300 6600
13300 5800 13300 6600
Connection ~ 13300 6300
Wire Wire Line
11900 14400 11900 14500
Wire Wire Line
11900 14500 11900 14600
11900 14400 11900 14600
Wire Wire Line
12400 14500 12400 14600
Connection ~ 12400 14500
@ -3297,9 +3179,7 @@ Connection ~ 12150 14500
Wire Wire Line
3700 5100 3800 5100
Wire Wire Line
3800 5000 3800 5100
Wire Wire Line
3800 5100 3800 5200
3800 5000 3800 5200
Wire Wire Line
3700 5000 3800 5000
Connection ~ 3800 5100
@ -3318,28 +3198,18 @@ Wire Wire Line
Wire Wire Line
1100 4400 1500 4400
Wire Wire Line
2300 3700 2400 3700
Wire Wire Line
2400 3700 2500 3700
2300 3700 2500 3700
Wire Wire Line
2500 3400 2400 3400
Wire Wire Line
2400 3400 2400 3700
Connection ~ 2400 3700
Wire Wire Line
2200 2900 2300 2900
Wire Wire Line
2300 2900 2500 2900
2200 2900 2500 2900
Wire Wire Line
2300 2350 2300 2900
Wire Wire Line
3700 2900 3800 2900
Wire Wire Line
3800 2900 4050 2900
Wire Wire Line
4050 2900 4350 2900
Wire Wire Line
4350 2900 4600 2900
3700 2900 4600 2900
Wire Wire Line
3800 2900 3800 3000
Connection ~ 3800 2900
@ -3352,9 +3222,7 @@ Wire Wire Line
Wire Wire Line
4050 4600 4050 4500
Wire Wire Line
4050 3900 4050 4100
Wire Wire Line
4050 4100 4050 4200
4050 3900 4050 4200
Connection ~ 4050 4100
Wire Wire Line
3700 4000 4350 4000
@ -3378,13 +3246,7 @@ Wire Wire Line
Wire Wire Line
18600 7750 19200 7750
Wire Wire Line
19200 4900 18600 4900
Wire Wire Line
11900 14500 12150 14500
Wire Wire Line
12150 14500 12400 14500
Wire Wire Line
12400 14500 13400 14500
11900 14500 13400 14500
Wire Wire Line
15600 12600 16400 12600
Wire Wire Line
@ -3395,9 +3257,7 @@ Connection ~ 14700 12800
Wire Wire Line
14700 12700 14800 12700
Wire Wire Line
14700 12700 14700 12800
Wire Wire Line
14700 12800 14700 13000
14700 12700 14700 13000
Wire Wire Line
14800 12800 14700 12800
Wire Wire Line
@ -3487,49 +3347,29 @@ Wire Wire Line
4350 4000 4350 2900
Connection ~ 4350 2900
Wire Wire Line
6950 6500 6950 6600
Wire Wire Line
6950 6600 6950 6950
Wire Wire Line
6950 6950 6950 7050
Wire Wire Line
6950 7050 6950 7150
6950 6500 6950 7150
Wire Wire Line
6950 6600 7100 6600
Connection ~ 6950 6600
Wire Wire Line
7350 10600 7350 10500
Wire Wire Line
6300 14300 6400 14300
6300 14300 6500 14300
Wire Wire Line
6400 14300 6500 14300
Wire Wire Line
5500 14300 5600 14300
Wire Wire Line
5600 14300 5700 14300
5500 14300 5700 14300
Wire Wire Line
5850 14100 5600 14100
Wire Wire Line
5600 14100 5600 14300
Wire Wire Line
5600 14300 5600 14450
5600 14100 5600 14450
Connection ~ 5600 14300
Wire Wire Line
6150 14100 6400 14100
6150 14100 6650 14100
Wire Wire Line
6400 14100 6650 14100
Wire Wire Line
6400 14100 6400 14300
Wire Wire Line
6400 14300 6400 14450
6400 14100 6400 14450
Connection ~ 6400 14300
Connection ~ 6400 14100
Wire Wire Line
7100 14300 7200 14300
Wire Wire Line
7200 14300 7600 14300
Wire Wire Line
7600 14300 7800 14300
7100 14300 7800 14300
Wire Wire Line
7200 14300 7200 14100
Wire Wire Line
@ -3545,17 +3385,11 @@ Connection ~ 7600 14300
Wire Wire Line
4600 3000 4500 3000
Wire Wire Line
4500 3000 4500 3100
4500 3000 4500 3200
Wire Wire Line
4500 3100 4500 3200
5300 3100 6100 3100
Wire Wire Line
5300 3100 5700 3100
Wire Wire Line
5700 3100 6100 3100
Wire Wire Line
5400 2800 5400 2900
Wire Wire Line
5400 2900 5400 3250
5400 2800 5400 3250
Wire Wire Line
5400 3600 5400 3550
Wire Notes Line
@ -3573,15 +3407,7 @@ Wire Wire Line
Wire Wire Line
18600 7650 19200 7650
Wire Wire Line
16300 3150 16300 3250
Wire Wire Line
16300 3250 16300 3350
Wire Wire Line
16300 3350 16300 3450
Wire Wire Line
16300 3450 16300 3550
Wire Wire Line
16300 3550 16300 3700
16300 3150 16300 3700
Wire Wire Line
16200 4400 16200 4300
Wire Wire Line
@ -3617,11 +3443,7 @@ Wire Wire Line
Connection ~ 16300 3350
Connection ~ 15250 4000
Wire Wire Line
15150 4000 15250 4000
Wire Wire Line
15250 4000 15500 4000
Wire Wire Line
15500 4000 16400 4000
15150 4000 16400 4000
Wire Wire Line
15850 4700 15850 4100
Wire Wire Line
@ -3631,13 +3453,7 @@ Wire Wire Line
Wire Wire Line
12800 6500 13500 6500
Wire Wire Line
16300 7600 16300 7700
Wire Wire Line
16300 7700 16300 7800
Wire Wire Line
16300 7800 16300 7900
Wire Wire Line
16300 7900 16300 8000
16300 7600 16300 8000
Wire Wire Line
16300 7600 16400 7600
Wire Wire Line
@ -3660,21 +3476,7 @@ Wire Wire Line
Wire Wire Line
15000 3250 15000 3350
Wire Wire Line
14450 3250 14650 3250
Wire Wire Line
14650 3250 15000 3250
Wire Wire Line
15000 3250 15250 3250
Wire Wire Line
15250 3250 15500 3250
Wire Wire Line
15500 3250 15750 3250
Wire Wire Line
15750 3250 16000 3250
Wire Wire Line
16000 3250 16300 3250
Wire Wire Line
16300 3250 16400 3250
14450 3250 16400 3250
Connection ~ 15000 3250
Wire Wire Line
20650 7350 20550 7350
@ -3696,18 +3498,12 @@ Wire Wire Line
Wire Wire Line
19200 7050 18600 7050
Wire Wire Line
21250 13600 21250 13800
Wire Wire Line
21250 13800 21250 14100
Wire Wire Line
21250 14100 21250 14350
21250 13600 21250 14350
Wire Wire Line
21350 13800 21250 13800
Connection ~ 21250 13800
Wire Wire Line
13300 14700 13300 14900
Wire Wire Line
13300 14900 13300 15000
13300 14700 13300 15000
Wire Wire Line
13300 14900 13400 14900
Wire Wire Line
@ -3747,13 +3543,9 @@ Wire Wire Line
Wire Wire Line
13300 13900 13300 13800
Wire Wire Line
12400 13800 13300 13800
12400 13800 13400 13800
Wire Wire Line
13300 13800 13400 13800
Wire Wire Line
12400 13700 12400 13800
Wire Wire Line
12400 13800 12400 13900
12400 13700 12400 13900
Connection ~ 13300 13800
Connection ~ 12400 13800
Wire Wire Line
@ -3763,15 +3555,11 @@ Wire Wire Line
Connection ~ 13300 14900
Connection ~ 15700 12400
Wire Wire Line
15700 12250 15700 12400
Wire Wire Line
15700 12400 15700 12800
15700 12250 15700 12800
Wire Wire Line
15700 12800 15600 12800
Wire Wire Line
14700 12300 14700 12400
Wire Wire Line
14700 12400 14700 12500
14700 12300 14700 12500
Wire Wire Line
14700 12500 14800 12500
Wire Wire Line

Wyświetl plik

@ -1833,7 +1833,7 @@ F 4 "10V" H 5300 8600 60 0001 C CNN "Specs"
1 0 0 -1
$EndComp
Text Label 18500 12100 0 60 ~ 0
V_BOOST
USB_ID
$Comp
L +1V8 #PWR061
U 1 1 57AF31A6
@ -2576,17 +2576,6 @@ F 3 "" H 15850 4700 50 0000 C CNN
1 0 0 -1
$EndComp
$Comp
L GND #PWR093
U 1 1 58F4F4C7
P 18700 4250
F 0 "#PWR093" H 18700 4000 50 0001 C CNN
F 1 "GND" H 18700 4100 50 0000 C CNN
F 2 "" H 18700 4250 50 0000 C CNN
F 3 "" H 18700 4250 50 0000 C CNN
1 18700 4250
0 -1 -1 0
$EndComp
$Comp
L GND #PWR094
U 1 1 58F58554
P 16300 8000
@ -3245,8 +3234,6 @@ Wire Wire Line
21000 13500 21350 13500
Wire Wire Line
18600 7750 19200 7750
Wire Wire Line
19200 4900 18600 4900
Wire Wire Line
11900 14500 13400 14500
Wire Wire Line
@ -3451,7 +3438,7 @@ Wire Wire Line
Wire Wire Line
15850 4100 16400 4100
Wire Wire Line
18600 4250 18700 4250
18600 4250 19200 4250
Wire Wire Line
12800 6500 13500 6500
Wire Wire Line
@ -3683,4 +3670,6 @@ Wire Wire Line
Connection ~ 1700 9950
Wire Wire Line
1700 9950 1700 9550
Text Label 19200 4250 2 60 ~ 0
USB_ID
$EndSCHEMATC

Wyświetl plik

@ -1,259 +1,259 @@
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
endif
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT =
endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -fno-rtti
endif
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = yes
endif
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
# If enabled, this option makes the build process faster by not compiling
# modules not used in the current configuration.
ifeq ($(USE_SMART_BUILD),)
USE_SMART_BUILD = yes
endif
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Stack size to be allocated to the Cortex-M process stack. This stack is
# the stack used by the main() thread.
ifeq ($(USE_PROCESS_STACKSIZE),)
USE_PROCESS_STACKSIZE = 0x400
endif
# Stack size to the allocated to the Cortex-M main/exceptions stack. This
# stack is used for processing interrupts and exceptions.
ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
USE_EXCEPTIONS_STACKSIZE = 0x400
endif
# Enables the use of FPU (no, softfp, hard).
ifeq ($(USE_FPU),)
USE_FPU = no
endif
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Define project name here
PROJECT = ch
# Imported source files and paths
CHIBIOS = ChibiOS
# Startup files.
include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f4xx.mk
# HAL-OSAL files (optional).
include $(CHIBIOS)/os/hal/hal.mk
include $(CHIBIOS)/os/hal/ports/STM32/STM32F4xx/platform.mk
include board/board.mk
include $(CHIBIOS)/os/hal/osal/rt/osal.mk
# RTOS files (optional).
include $(CHIBIOS)/os/rt/rt.mk
include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk
# Other files (optional).
include $(CHIBIOS)/test/rt/test.mk
include $(CHIBIOS)/os/hal/lib/streams/streams.mk
include $(CHIBIOS)/os/various/shell/shell.mk
# Define linker script file here
LDSCRIPT= board/STM32F413xH.ld
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(STARTUPSRC) \
$(KERNSRC) \
$(PORTSRC) \
$(OSALSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC) \
$(TESTSRC) \
$(SHELLSRC) \
$(CHIBIOS)/os/hal/lib/streams/memstreams.c \
$(CHIBIOS)/os/hal/lib/streams/chprintf.c \
modules/tracking.c \
modules/position.c \
modules/image.c \
modules/log.c \
protocols/ssdv/ssdv.c \
protocols/ssdv/rs8.c \
protocols/aprs/aprs.c \
protocols/aprs/ax25.c \
protocols/morse/morse.c \
drivers/wrapper/pi2c.c \
drivers/wrapper/padc.c \
drivers/wrapper/ptime.c \
drivers/ublox.c \
drivers/si4464.c \
drivers/bme280.c \
drivers/pac1720.c \
drivers/ov2640.c \
drivers/ov5640.c \
drivers/flash/flash.c \
drivers/flash/helper.c \
drivers/flash/ihex.c \
debug.c \
radio.c \
sleep.c \
modules.c \
math/base.c \
math/sgp4.c \
math/geofence.c \
config.c \
watchdog.c \
usbcfg.c \
main.c
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC =
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACSRC =
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACPPSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCPPSRC =
# List ASM source files here
ASMSRC =
ASMXSRC = $(STARTUPASM) $(PORTASM) $(OSALASM)
INCDIR = $(CHIBIOS)/os/license \
$(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \
$(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \
$(SHELLINC)
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
MCU = cortex-m4
#TRGT = arm-elf-
TRGT = arm-none-eabi-
CC = $(TRGT)gcc
CPPC = $(TRGT)g++
# Enable loading with g++ only if you need C++ runtime support.
# NOTE: You can use C++ even without C++ support if you are careful. C++
# runtime support makes code size explode.
LD = $(TRGT)gcc
#LD = $(TRGT)g++
CP = $(TRGT)objcopy
AS = $(TRGT)gcc -x assembler-with-cpp
AR = $(TRGT)ar
OD = $(TRGT)objdump
SZ = $(TRGT)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary
# ARM-specific options here
AOPT =
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
# Define C warning options here
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
# Define C++ warning options here
CPPWARN = -Wall -Wextra -Wundef
#
# Compiler settings
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
# Define ASM defines here
UADEFS =
# List all user directories here
UINCDIR = modules/ drivers/ drivers/wrapper/ protocols/aprs \
protocols/ssdv protocols/morse math/ drivers/flash/
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS = -lm
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC
include $(RULESPATH)/rules.mk
burn:
st-flash write build/$(PROJECT).bin 0x08000000
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
endif
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT = -std=c11
endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -fno-rtti
endif
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = yes
endif
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
# If enabled, this option makes the build process faster by not compiling
# modules not used in the current configuration.
ifeq ($(USE_SMART_BUILD),)
USE_SMART_BUILD = yes
endif
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Stack size to be allocated to the Cortex-M process stack. This stack is
# the stack used by the main() thread.
ifeq ($(USE_PROCESS_STACKSIZE),)
USE_PROCESS_STACKSIZE = 0x400
endif
# Stack size to the allocated to the Cortex-M main/exceptions stack. This
# stack is used for processing interrupts and exceptions.
ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
USE_EXCEPTIONS_STACKSIZE = 0x400
endif
# Enables the use of FPU (no, softfp, hard).
ifeq ($(USE_FPU),)
USE_FPU = no
endif
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Define project name here
PROJECT = ch
# Imported source files and paths
CHIBIOS = ChibiOS
#CHIBIOS = C:\ChibiStudio\chibios_trunk
# Startup files.
include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f4xx.mk
# HAL-OSAL files (optional).
include $(CHIBIOS)/os/hal/hal.mk
include $(CHIBIOS)/os/hal/ports/STM32/STM32F4xx/platform.mk
include board/board.mk
include $(CHIBIOS)/os/hal/osal/rt/osal.mk
# RTOS files (optional).
include $(CHIBIOS)/os/rt/rt.mk
include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk
# Other files (optional).
include $(CHIBIOS)/test/rt/test.mk
include $(CHIBIOS)/os/hal/lib/streams/streams.mk
include $(CHIBIOS)/os/various/shell/shell.mk
# Define linker script file here
LDSCRIPT= board/STM32F413xH.ld
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(STARTUPSRC) \
$(KERNSRC) \
$(PORTSRC) \
$(OSALSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC) \
$(TESTSRC) \
$(SHELLSRC) \
$(CHIBIOS)/os/hal/lib/streams/memstreams.c \
$(CHIBIOS)/os/hal/lib/streams/chprintf.c \
threads/tracking.c \
threads/position.c \
threads/image.c \
threads/log.c \
protocols/ssdv/ssdv.c \
protocols/ssdv/rs8.c \
protocols/aprs/aprs.c \
protocols/aprs/ax25.c \
protocols/morse/morse.c \
drivers/wrapper/pi2c.c \
drivers/wrapper/padc.c \
drivers/wrapper/ptime.c \
drivers/ublox.c \
drivers/si4464.c \
drivers/bme280.c \
drivers/pac1720.c \
drivers/ov5640.c \
drivers/flash/flash.c \
drivers/flash/helper.c \
drivers/flash/ihex.c \
debug.c \
radio.c \
sleep.c \
threads/threads.c \
math/base.c \
math/sgp4.c \
math/geofence.c \
config.c \
watchdog.c \
usbcfg.c \
main.c
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC =
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACSRC =
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACPPSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCPPSRC =
# List ASM source files here
ASMSRC =
ASMXSRC = $(STARTUPASM) $(PORTASM) $(OSALASM)
INCDIR = $(CHIBIOS)/os/license \
$(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \
$(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \
$(SHELLINC)
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
MCU = cortex-m4
#TRGT = arm-elf-
TRGT = arm-none-eabi-
CC = $(TRGT)gcc
CPPC = $(TRGT)g++
# Enable loading with g++ only if you need C++ runtime support.
# NOTE: You can use C++ even without C++ support if you are careful. C++
# runtime support makes code size explode.
LD = $(TRGT)gcc
#LD = $(TRGT)g++
CP = $(TRGT)objcopy
AS = $(TRGT)gcc -x assembler-with-cpp
AR = $(TRGT)ar
OD = $(TRGT)objdump
SZ = $(TRGT)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary
# ARM-specific options here
AOPT =
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
# Define C warning options here
CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
# Define C++ warning options here
CPPWARN = -Wall -Wextra -Wundef
#
# Compiler settings
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
# Define ASM defines here
UADEFS =
# List all user directories here
UINCDIR = threads/ drivers/ drivers/wrapper/ protocols/aprs \
protocols/ssdv protocols/morse math/ drivers/flash/
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS = -lm
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC
include $(RULESPATH)/rules.mk
burn:
st-flash write build/$(PROJECT).bin 0x08000000

Wyświetl plik

@ -1,307 +0,0 @@
# Software for Pecan Balloon Trackers
This is the software, that runs the [Pecan Pico 7 balloon trackers](https://github.com/DL7AD/pecanpico7). Since the new trackers can be used in many ways, this software is designed for multiple purpose. It can be used for:
- Image transmissions
- [SSDV](https://ukhas.org.uk/guides:ssdv) as 2FSK modulation (RTTY)
- [APRS](http://aprs.org)-encoded [SSDV](https://ukhas.org.uk/guides:ssdv) as AFSK or 2GFSK modulation
- Position/Telemetry transmission (GPS)
- by [APRS](http://aprs.org) AFSK or 2GFSK
- 2FSK (RTTY)
- CW (morse)
This software may also feature
- Amateur satellite communication links
- Full duplex repeater for digital modulations (up 70cm, down 2m)
- Ground control access via radio link
###### Transmitted Test Images
Here are some images which were transmitted by [SSDV](https://ukhas.org.uk/guides:ssdv). These QVGA-images were captured by an [Omnivision OV9655](http://www.waveshare.com/wiki/OV9655_Camera_Board).
![Test image 1](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/sample_pictures/test2.jpg)
![Test image 2](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/sample_pictures/test3.jpg)<br>
###### Targets
Currently there is only one target supported by the Software. Pecan Pico 7a will be succeeded by Pecan Pico 7b due to some mayor errors ([See known PCB errors](https://github.com/DL7AD/pecanpico7/tree/7a)).
<img src="https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/targets/pecanpico7a.jpg" width="320" alt="Pecan Pico 7a"><br>[Pecan Pico 7a](https://github.com/DL7AD/pecanpico7/tree/7a)
###### Features
All features can be combined as required for the user. Currently the software is incomplete but basic features can be used:
- Image capturing (just QVGA at the moment) and transmission by SSDV
- Reception of positions (by ublox MAX8 GNSS module)
- Measurement of temperature/air-pressure/humidity (by onboard BME280)
- Measurement of temperature/air-pressure/humidity (by external BME280)
- Measurement of the power consumption (battery charging and discharging)
- Position and telemetry transmissions by 2FSK (or RTTY)
- High altitude mode (up to 50km altitude)
- Debugging (serial connection)
- APRS encoding (AFSK, 2GFSK)
- APRS SSDV-encoded Image Transmission
What currently doesn't work (but planned):
- High resolution image capture (just QVGA at the moment)
- Logging and log transmissions
- Amateur satellite orbit calculations and transmissions
- Power save modes
- Storage of telemetry and image data on the microSD card
It is currently not planned to implement video capture. The STM32F4 doesn't have sufficient computing power for compression encoding. However the new pin-compabile [STM32F7x7](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1858) supports JPEG encoding. So video capture may be possible in future.
###### Dependency installations/ Compiling/ Flashing
In order to get the hardware working, you'll need a programmer. There are several devices on the market but I personally use the [STM32F429I-DISCOVERY](http://www.st.com/st-web-ui/static/active/en/resource/technical/document/user_manual/DM00093903.pdf) demo board. The discovery board is basically a programmer with a demo target (microcontroller) attached to it. It's designed in a way so you can either flash the demo target or the Pecan Pico 7 by switching specific jumpers on the board. You may get the programmer at [Mouser](http://www2.mouser.com/search/ProductDetail.aspx?R=0virtualkey0virtualkeySTM32F429I-DISC1), [Farnell](http://www.newark.com/stmicroelectronics/stm32f429i-disc1/dev-board-stm32f429zi-advanced/dp/72Y1169), [Digikey](http://www.digikey.de/product-detail/de/stmicroelectronics/STM32F429I-DISCO/497-13898-ND/4310131) or on Ebay for roughly $30. You'll need to connect the programmer as follows. Don't forget to remove the jumpers which connect the programmer with the demo target.
You'll also need some software dependencies.
- _GCC_ to build binary files
- _ST-Flash_ to flash the binaries to your target
There's a [great guide](http://www.wolinlabs.com/blog/linux.stm32.discovery.gcc.html) on the Web which I've followed myself to set up everything. They are using a different demo board but it works similarily.
Then you'll need the source code for the tracker:
```
git clone --recursive https://github.com/DL7AD/pecan-stm32f429.git
cd pecan-stm32f429
make
make flash
```
A _build_ folder will be created automatically. The software can be configured/customized in the [config.h](config.h).
###### How to connect the programmer to the Pecan Pico 7 PCB
<img src="https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/programmer.jpg" width="400" alt="Programmer connected to Pecan Pico 7">
# Technical Description
Technical description below...
# Modules
This section describes the different function blocks in the software and how they interact which each other. The software provides multiple modules, which supply the main functionality of the tracker like transmitting positions or images. Modules are basically [threads in the ChibiOS RTOS](http://www.chibios.org/dokuwiki/doku.php?id=chibios:howtos:createthread). Modules can be:
- POSITION - for transmining position and telemetry
- IMAGE - for transmitting images
- LOG - for transmitting logs
There are two more specific modules which are started automatically be the software at run time. They are necessary to handle input/output jobs.
- TRACKING (or tracking manager) - collects all the data from the periphery (BME280, GPS, etc.) and provides them (as struct) to other modules in the software.
- RADIO - provides the two radios which are available. This module receives internal messages from other modules which intend to send packets via VHF or UHF.
The following diagram shows how the modules interact with each other.
```
DRIVER | DATA PROCESSING MODULES | DRIVER
------------+--------------------------------------------------------------+------------------
MPU9250 ---+ +---> SATELLITE ---+
| | |
BME280 ----+---> TRACKING Manager -----+---> POSITION ----+----> RADIO ---+---> RADIO1 (2m)
| | | |
MAX -------+ +-----> LOG -------+ +---> RADIO2 (70cm)
|
OV9655 or ----------------------------------> IMAGE ------+
OV2640
```
## Module configuration
In order to use these modules, they can be configured in the MOULDES() macro in the [configuration file](config.h). All modules can be configured except of TRACKING and RADIO. They have no configurable parameters because they get their parameters from other modules in run time. The following example shows how an image module and a position module can be configured.
```
MODULES() { \
MODULE_IMAGE (TX_CONTINUOSLY, SLEEP_WHEN_BATT_BELOW_3V5, CUSTOM_FREQ, 10, PROT_SSDV_2FSK ); \
MODULE_POSITION(WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_3V0, CUSTOM_FREQ, 10, PROT_UKHAS_2FSK); \
}
```
As you can see, there are several parameters which can be used to configure a specific module:
**Parameter 1** specifies in which cycle the modules shall send their data. This can be a time in seconds. It's also allowed to indicate *TX_CONTINUOSLY* or *WAIT_FOR_TRACKING_POINT* in this field.<br>
*TX_CONTINUOSLY* - Transmit new data as soon as the previous frame is compled<br>
*WAIT_FOR_TRACKING_POINT* - Transmit when the tracking manager generates a new track point<br>
**Parameter 2** specifies the sleep mode. Note this function is currently not implemented. The modules will disregard this field.<br>
**Parameter 3** specifies a frequency method. Currently *CUSTOM_FREQ* or *APRS_REGION_FREQ* can be used. The frequencies are dynamic and set in [radio.c](radio.c). *CUSTOM_FREQ* is set to 434.500MHz and *APRS_REGION_FREQ* is set to 144.800MHz as the default. A geofencing algorithm is implemented for *APRS_REGION_FREQ* in order to find out the specific APRS region frequency by the actual location.<br>
**Parameter 4** specifies the transmission power in dBm. If the value is set to a point which the radio is unable to reach (or would damage the PCB) the actual value may be limited by the RADIO module.<br>
**Parameter 5** specifies the protocol and modulation. While you can use *PROT_UKHAS_2FSK*, *PROT_APRS_AFSK* or *PROT_RAW_CW* for the POSITION module, the IMAGE module can accept *PROT_SSDV_2FSK*, *PROT_SSDV_APRS_AFSK* and *PROT_SSDV_APRS_2GFSK* at the moment.
Note not all of the parameters can be used for every individual module because some configurations may not make sense e.g. CW modulation for transmitting images. You can find an exact documentation in the [configuration file](config.h) which specific parameters can be applied to the the modules.
## Concurrent use of modules
Modules can be merged in various combinations. However modules can be also understood as classes whose instances can be used multiple times. So if you want to transmit telemetry and positions by RTTY and APRS at the same time, you may configure modules multiple times. The following example shows, how the position/telemetry is sent on a custon frequency while it's also transmitted by APRS on the regional APRS frequency and CW.
```
MODULES() { \
MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_2V5, CUSTOM_FREQ, 10, PROT_UKHAS_2FSK); \
MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_2V5, APRS_REGION_FREQ, 10, PROT_APRS_AFSK ); \
MODULE_POSITION (WAIT_FOR_TRACKING_POINT, SLEEP_WHEN_BATT_BELOW_3V0,CUSTOM_FREQ, 10, PROT_RAW_CW ); \
}
```
## Module TRACKING (or tracking manager)
The tracking manager keeps an eye on all peripheral components that provide data. With exception of the camera, the tracking manager collects the data of:
- BME280 (airpressure, humidity, temperature)
- MAX7/8 (GPS/GLONASS receiver)
- PAC1720 (Power consumption tracking)
The data of these components will be collected at a specific cycle time and will be provided to other modules in a struct [trackPoint_t](modules/tracking.h). The most recently sampled data can be accessed by other modules by [getLastTrackPoint()](modules/tracking.h).
## Module RADIO
The module RADIO handles the two radios which are available on the Pecan Pico 7. It initializes a mailbox in which messages (or data packets) can be written by other functions in order to use the radios. If a module wants to send a packet on HF, it may generate a [radioMSG_t](modules/radio.h) struct and pass is over to [transmitOnRadio()](modules/radio.h). The radio will determine a suitable radio by the frequency which is set in the struct. Once the radio is free, the module will send the message. If the radio is already in use, the RADIO module may buffer up to 3 more messages in a message queue. The queue is cleared by the FIFO concept. While only 3 messages can be stored in the queue, the maximum generation-transmission delay is quite small (<15sec usually). [transmitOnRadio()](modules/radio.h) may return *true* if the message is sent to the queue. *False* indicates the queue is full. If a module wants to send the message, it has to try again by calling transmitOnRadio() again. While there is no priority management at the moment, the modules have to try more or less aggressivly to insert its messages to the radio queue. This strategy more a workaround than a solution. Priority management would be better.
###### 2FSK modulation
2FSK is the name for RTTY in the software because this modulation type also transmits start and stop bit (8n2). The modulation is managed by a software timer because the timing dont have to be that accurate (compared to AFSK and GFSK). The modulation is passed over to the Si446x by a GPIO pin. As the pin is toggled, the transmitter will change its frequency.
###### CW modulation
CW is moduled like 2FSK. The transmitter is just set into the mode OOK (On Off Keying).
###### APRS AFSK modulation
The APRS modulation is a Si446x-specific hack because the Si Chip is unable to do FM natively. But Si446x chips are able to modulate 2GFSK. GFSK has a much smoother frequency change than its the case at FSK. The modulation is afftected on a GPIO pin at the transmitter which is connected to the Microcontroller. As this GPIO pin is switched the transmitter also changes its frequency. This characteristic knowlegde can be used to implement FM. If the pin is toggled 4400 times in a second, a 2200Hz tone can be modulated at FM. The 1200Hz tone can be modulated by toggling the pin 2400 times in a second. The modulation is additionally filtered in the Si446x chips with a FIR filter, so harmonics above 2200Hz are filtered.
Unfortunately the modulation requires an accurate timing. If the timing is not managed correctly at the microcontroller, the modulation gets distorted. To ensure accurate timings, all threads and interrupts are disabled at the microcontroller so the pin is toggled at the right time.
###### APRS 2GFSK modulation
The 2GFSK modulation is quite simple because the Si446x supports 2 and 4GFSK natively. Like it`s done at AFSK, the modulation is generated by the microcontroller which requires timing. So the microcontroller cant do anything else at the actual transmission.
## Module POSITION
This module handles the radio transmission encoding for all position/telemetry packets. It is able to encode CW, APRS and 2FSK (or RTTY). A cycle can be continuosly, time-specific or be triggered by the TRACKING manager when a new track point is published. This modules can be configured in the MODULES() marco: `MODULE_POSITION(trigger, sleep, frequency, power, protocol)`.
| Field | Meaning | Possible options
| --------- | ------- | ----------------
| trigger | Cycle time or trigger type for transmission | `TX_CONTINUOSLY` or `WAIT_FOR_TRACKING_POINT` or Integer (in seconds)
| sleep | Sleep method | `SLEEP_WHEN_BATT_BELOW_2V9` or `SLEEP_WHEN_BATT_BELOW_3V0` ... `SLEEP_WHEN_BATT_BELOW_4V1` or `SLEEP_WHEN_ISS_NOT_VISIBLE`
| frequency | Frequency method | `CUSTOM_FREQ` or `APRS_REGION_FREQ` or `APRS_ISS_FREQ`
| power | Radio power (in dBm) | Integer
| protocol | Protocol | `PROT_APRS_2GFSK` or `PROT_APRS_AFSK` or `PROT_UKHAS_2FSK` or `PROT_RAW_CW` or `PROT_APRSCONFIG_AFSK` or `PROT_APRSCONFIG_2GFSK`
For a detailed explanation about the option macros see [config.h](config.h).
###### APRS Encoding
This software makes use of the amateur radio [APRS system (Automatic Packet Reporting system)](http://aprs.org), which has been invented by Bob Bruninga in 1992. Since APRS has a [great coverage](http://aprs.fi/#!mt=roadmap&z=3) in the US and Europe, is it very useful for balloon tracking. At the moment its also the most convenient system which can be used.
The software itself implements the compressed format. This software is actually not recommended but supported by all devices. Since there has been no replacement and the raw format is quiet big, this is still the best encoding option. The format also supports 5 different 13bit-telemetry transmissons which are used for:
- Battery voltage
- Solar voltage
- Temperature (in Celcius)
- Battery charge (in mW)
- Battery discharge (in mW)
The software also might display the GPS-satellite count, the TTFF (time to first fix) and GPS-LOSS counter in the comment field. Note the telemetry fields are mathematically encoded and the receives have to know, how they can decode this telemetry. In order to transmit the configuration by the tracker itself, place an additional POSITION modules in the MODULES() marco in the [config.h](config.h) with the protocol `PROT_APRSCONFIG_AFSK`. It will transmit all the necessary decoding information for the receiver.
`MODULE_POSITION (3600, SLEEP_WHEN_BATT_BELOW_3V0, APRS_REGION_FREQ, 10, PROT_APRSCONFIG_AFSK);`
All APRS messages will be encoded by the POSITION module for the RADIO module in a bitstream (1 bit = 1 Symbol). The packets already contain the AX.25 specific bitstuffing, NRZ-I encoding (for AX.25 AFSK) and scrambling (for AX.25 2GFSK).
![APRS Packet](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/aprs.jpg)<br>
Transmitted APRS packet displayed by [APRS.fi](http://aprs.fi)
###### 2FSK Encoding
2FSK messages will be encoded especially for the [UKHAS system](https://ukhas.org.uk/guides:tracking_guide). Unlike AFSK, this modulation can be only received with a SSB receiver. See on the [UKHAS Tracking Guide](https://ukhas.org.uk/guides:tracking_guide) which receivers can be used. While UKHAS is very flexible, the packet format can be configured in the configuration file by the UKHAS_FORMAT marco. The format contains a set of variables which can be changed user specific.
`#define UKHAS_FORMAT "<CALL>,<ID>,<TIME>,<LAT>,<LON>,<ALT>,<SATS>,<TTFF>,<VBAT>,<VSOL>,<CHARGE>,<IPRESS>,<ITEMP>,<IHUM>,<EPRESS>,<ETEMP>,<EHUM>"`
The preamble ($$$$$) in the front and CRC calculation at the end will be added by the software automatically. The output could be:
`$$$$$D-9,5,00:03:15,52.4999910,13.5000084,19948,9,16,4.33,0.00,-1.114,100271,36.1,22,0,0.0,0*8B0B`
| Data | Explanation
| ----------- | -----------
| $$$$$ | Snchronisation (added by the software)
| D-9, | Payload name (See UKHAS_CALLSIGN info [config.h](config.h))
| 5, | Sequence ID (incremented each TRACKING manager cycle)
| 00:03:15, | GPS Time (when this fix has been sampled)
| 52.4999910, | GPS Latitude
| 13.5000084, | GPS Longitude
| 19948, | GPS altitude in meters
| 9, | GPS satellites
| 16, | TTFF (in seconds)
| 4.33, | Battery voltage (in volts)
| 0.00, | Solar voltage (in volts)
| -1.114, | Power consumption (in Watts)
| 100271, | Air Pressure of onbard BME280 (in Pascal)
| 36.1, | Temperature (in Celcius)
| 22, | Humidity (in %)
| 0, | Air Pressure of offbard BME280 (in Pascal)
| 0.0, | Temperature of offbard BME280 (in Celcius)
| 0 | Humidity of offbard BME280 (in %)
| *8B0B | CRC (Calculated by software)
The CRC calculation uses crc16-ccitt (0x1021). A detailed documentation about the tags can be found in the [config.h](config.h).
![2FSK Packet](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/2fsk.jpg)<br>
Transmitted 2FSK packet displayed by [HABHUB](http://tracker.habhub.org)
###### CW Encoding (morse)
CW was implemented while some persons are not able to do APRS/2FSK or simply don't like it. Since no additional equipment is needed for decoding, the signal can be possibly picked up by someone else who don't know about the balloon launch and how to decode it. CW might be also useful for [ARDF (Amateur radio direction finding)](https://en.wikipedia.org/wiki/Amateur_radio_direction_finding) if the GPS failed to receive.
Like 2FSK, CW encoding can be configured flexible by the CW_FORMAT macro. All configuration tags which are known for 2FSK can be also used for CW. The POSITION module may encode A-Z, 0-9, "." and space. All other characters will be dismissed e.g. "D-9" => "D9". `#define CW_FORMAT "<CALL> <LOC> <ALT>M PSE QSL"` will encode a tracking point as `D9 JO62RM 19948M PSE QSL`. The message will be encoded binary as 1/10sec bitstream (1: Tone, 0: No Tone). This message is then sent to the RADIO module.
Note CW is no format which is very useful for computer-enabled processing, so there is no service like [APRS.fi](http://aprf.fi) or the tracker service by [HABHUB](http://tracker.habhub.org) for CW.
![CW Packet](https://raw.githubusercontent.com/DL7AD/pecan-stm32f429/master/doc/cw.jpg)<br>
Transmitted CW message contains [Maidenhead Locator](https://en.wikipedia.org/wiki/Maidenhead_Locator_System) and altitude displayed by [DL-FLDigi](https://ukhas.org.uk/projects:dl-fldigi)
## Module IMAGE
The module IMAGE handles the image transmission of the Pecan Pico 7 and encodes it for the radio. At the moment, there is only enough memory available to handle one image thread. It's actually possible to run multiple instances but the instances will interfere each other due to multiple use of the same memory. There are 2 (and a half) methods which can be used to transmit the pictures.
- SSDV transmitted by 2FSK
- Modified SSDV-packet in an APRS packet transmitted by AFSK or 2GFSK
Raw SSDV is described [here](https://ukhas.org.uk/guides:ssdv). Mostly SSDV is transmitted by 2FSK 600baud which is quiet slow. So this tracker is also able to transmit a modified SSDV packet in an APRS packet. Copared to 2FSK 600baud, APRS can be quiet fast or good in coverage (see the strategies below). The raw SSDV packet cotains 256byte on each packet and contains:
- Sync byte
- Packet type
- Callsign
- Image ID
- Packet ID
- Width
- Height
- Flags
- MCU offset
- MCU index
- Payload
- Checksum (CRC)
- FEC
If we put SSDV into an APRS packet, no everything must be transmitted because some fields get useless it the APRS packet. So dont transmit the _Sync byte_, the _Checksum_ and the _FEC_. The packet ends up with 219byte of payload data in the APRS packet. But transmitting binary data in an APRS packet could cause interference with the APRS protocol itself (and APRS-IS), so the data is then encoded by base91. By encoding the payload data is enlarged to ~270byte. The enlargement by base91 and protocol overhead by APRS sounds quiet awful. But RTTY also needs additional start and stop bits which is not neccessary at AFSK and 2GFSK, so you wont be able to transmit 600bit/s at 600baud. The packet overhead of the SSDV/APRS packets is rougly 26% depending on the preamble. This includes base91 encoding, additional protocol and preamble. RTTY therefore needs one start- and one stop-bit which generates 20% encoding overhead.
**Transmission Improvements:**
| Method | Symbol rate | Bit rate | Packet size\* | Packets per sec. | Transmission time, per image\*\*
| ---------- | ------------| ---------- | ------------- | ---------------- | --------------------------------
| RTTY | 600 baud | 480 bit/s | 256byte | 0.23 | 260 sec
| APRS AFSK | 1200 baud | 1200 bit/s | 350byte | 0.43 (+87%) | 140 sec
| APRS 2GFSK | 1200 baud | 9600 bit/s | 350byte | 3.43 (+1391%) | 17 sec
\* includes preamble
\*\* 60 packets per image, high duty cycle
Even if APRS encoded SSDV is incredible fast, it also has disadvantages. Reliability for example and the need of receiving equiment.
Here is a list of advantages and disadvantages of all the different methods and transmission strategies:
###### Transmit SSDV 2FSK modulated at 600baud with high duty cycle
Transmission speed: low, 4...5min for one QVGA image<br>
Reliability: very high due to FEC<br>
Equipment requirements: low, RTL-SDR and antenna<br>
Receiver coverage: low (stations must be informed by UKHAS usually)<br>
Other: Requires software which most people dont know how to use it
###### Transmit specialized SSDV APRS protocol, AFSK modulated with high duty cycle on a separate frequency
Transmission speed: faster, 2min for one QVGA image<br>
Reliability: low, it has no FEC (just CRC), packet size quiet large<br>
Equipment requirements: low, RTL-SDR or simple FM receiver and antenna<br>
Receiver coverage: low (stations must be informed)<br>
Other: Many people know how to receive APRS, special Habitat-Igating software necessary for SSDV server (software which sends the received packets to the server)
###### Transmit specialized SSDV APRS protocol, AFSK modulated with very low duty cycle on the navive APRS frequencies (144.800 / 144.390)
Transmission speed: very very slow, 30-45min for one QVGA image<br>
Reliability: low, it has no FEC (just CRC), packet size quiet large<br>
Equipment requirements: none (let the available stations receive)<br>
Receiver coverage: very very high (Europe, USA, Canada, Australia, China, Japan, etc.)<br>
Other: Making use of existing APRS netework, Igating software necessary which receives the packets from the APRS-IS an relay them to the SSDV-server
###### Transmit specialized SSDV APRS protocol, AFSK modulated with high duty cycle on a separate frequency
Transmission speed: very very fast, 15sec for one QVGA image, 8min for one Full HD image<br>
Reliability: medium, it has no FEC (just CRC)<br>
Equipment requirements: medium/high, RTL-SDR or special APRS9k6 TNC and receiver<br>
Receiver coverage: low (stations must be informed)<br>
Other: Many people know how to receive APRS (even 9k6), special Habitat-Igating software necessary for SSDV server (software which sends the received packets to the server)
# Included software
This software includes some other packages which I have used to make this project possible:
[JPEG Ant - JPEG compression for microcontrollers](https://sourceforge.net/projects/jpegant.berlios/)<br>
[ChibiOS - Real time operating system](http://chibios.org) ([Source](https://github.com/ChibiOS/ChibiOS))<br>
[SSDV - Slow Scan Digital Video](https://ukhas.org.uk/guides:ssdv) ([Source](https://github.com/fsphil/ssdv))

Wyświetl plik

@ -58,7 +58,7 @@
* The value one is not valid, timeouts are rounded up to
* this value.
*/
#define CH_CFG_ST_TIMEDELTA 2
#define CH_CFG_ST_TIMEDELTA 0
/** @} */

Wyświetl plik

@ -1,14 +1,11 @@
#include "config.h"
#include "aprs.h"
#include "debug.h"
module_conf_t config[9];
uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
/*
* Position module configuration description
* =========================================
*
* This module activates the position transmission of the tracker. If one of the position modules activated, it will activate the tracking manager which
* samples GPS position and atmospheric data. If you want to save position data to the flash memory, you have to enable at lease one position module. You can
* transmit positions either using 2FSK (RTTY) with the UKHAS standard, CW (Morse) or APRS (AFSK or 2GFSK).
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
@ -50,7 +47,10 @@ uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINOUSLY Continue continously (e.g. send new image once old image sent completely)
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* ============================== The following options are needed if protocol == PROT_APRS_AFSK or protocol == PROT_APRS_2GFSK ===============================
*
@ -73,13 +73,13 @@ uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
* (required) There are possible options:
* - TEL_SATS GPS Satellites
* - TEL_TTFF Time to first fix (amount of seconds which it needed to aquire a GPS fix)
* - TEL_VBAT Battery voltage
* - TEL_VSOL Solar voltage
* - TEL_VBAT Battery voltage (in mV)
* - TEL_VSOL Solar voltage (in mV)
* - TEL_PBAT Battery power (positive charge, negative discharge)
* - TEL_ISOL Solar short current (works only if USB is unattached)
* - TEL_PRESS Air pressure (by BME280)
* - TEL_TEMP Air temperature (by BME280)
* - TEL_HUM Air humidity (by BME280)
* - TEL_RBAT Battery impedance (in mOhm)
* - TEL_PRESS Air pressure (in Pa)
* - TEL_TEMP Air temperature (in degC*100)
* - TEL_HUM Air humidity (in %*10)
*
* aprs_conf.tel_enc bool The telemetry in the position packets do only contain the raw values. Receivers (like aprs.fi) dont know what these
* (default false) values stands for. So we must tell them (e.g. that value 1 is air pressure measured in Pascal). If set to true, the
@ -93,7 +93,7 @@ uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
*
* ============================================= The following options are needed if protocol == PROT_APRS_2GFSK ==============================================
*
* gfsk_conf.speed int 2GFSK speed. Following values have been tested successfully: 9600, 19200, 38400, 76800, 96000.
* gfsk_conf.speed int 2GFSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*
* ============================================= The following options are needed if protocol == PROT_UKHAS_2FSK ==============================================
@ -167,158 +167,336 @@ uint8_t ssdv_buffer[65535] __attribute__((aligned(1024)));
* - <LOC> Maidenhead locator
*/
/*
* Image module configuration description
* ======================================
*
* This module activates the transmission of pictures from the camera. You can use either SSDV transsmitted with 2FSK (RTTY) or APRS/SSDV. If you choose
* APRS/SSDV, generic APRS packets will be transmitted which can be received by any APRS receiver and decoded with a computer. In order to decode 2FSK/SSDV
* please use DL-FlDigi (https://ukhas.org.uk/projects:dl-fldigi). 2FSK/SSDV is very stable even with low receiption signals but it needs a SSB receiver. If
* you want to use APRS/SSDV you are going to need a packet receiver (like TH-D72, TH-D74 or FT2D or generic TNC). The packets can be decoded by a python
* script in the /decoder folder of this repository.
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
* applies:
* 20 dBm => 127
* 15 dBm => 40
* 10 dBm => 20
* 5 dBm => 12
* 0 dBm => 8
*
* protocol prot_t Possible Options:
* (required) - PROT_APRS_AFSK FM AFSK 1200baud transmission using the APRS/SSDV protocol
* this option requires aprs_conf to be set
* - PROT_APRS_2GFSK 2GFSK transmission using the APRS/SSDV protocol
* this option requires aprs_conf and gfsk_conf to be set
* - PROT_SSDV_2FSK 2FSK transmission using the SSDV protocol
* this option requires fsk_conf to be set
*
* frequency.type freq_type_t Defines the frequency type. This option will be FREQ_STATIC if not set.
* (default FREQ_STATIC) Possible Options:
* - FREQ_STATIC Static frequency taken from frequency.hz
* - FREQ_APRS_REGION Using the APRS region frequency. The tracker will change its frequency specificly to the region
* where it is located. e.g. 144.8MHz in Europe or 144.39MHz in the US. If the tracker doesnt know its
* position it takes its frequency from frequency.hz as default. Note that the tracker knows its
* position from its position log too. So it might use the last frequency which has been used before
* resetting it.
*
* frequency.hz int Frequency that this Module will transmit on (in Hz). The tracker can transmit in the 2m band. This value will be used
* (required) as default when frequency.type == FREQ_APRS_REGION and it doesnt know its position
*
* init_delay int Initial delay (in ms) before the module starts. This might be useful if you dont want to transmit so many APRS packets
* (default 0ms) at the same time on the APRS network. This option is optional. It will be 0ms if not set.
*
* trigger.type trigger_type_t Event at which this module is triggered to transmit. This option will be TRIG_ONCE if not set.
* (default TRIG_ONCE) Possible options:
* - TRIG_ONCE Trigger once and never again (e.g. transmit specific position packet only at startup)
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* ssdv_conf.callsign string The SSDV callsign (or stream identifier). This value helps the SSDV algorithm to assign packets from different images
* (required) to the right data set. This is helpful if multiple modules transmit different images at the same time.
*
* ssdv_conf.ram_buffer data Array of bytes which is used by the module for buffering the image
* (required)
*
* ssdv_conf.ram_size Size of buffer => sizeof(ssdv_conf.ram_buffer)
* (required)
*
* ssdv_conf.res resolution_t Resolution of the image
* (default RES_QVGA) Possible options:
* - RES_QVGA QVGA Resolution (320x240px)
* - RES_VGA VGA Resolution (640x480px)
* - RES_XGA XGA Resolution (1204x768px)
* - RES_UXGA UXGA Resolution (1600x1200px)
* - RES_MAX The module samples the highest resolution which fits into ssdv_conf.ram_buffer.
*
* ssdv_conf.redundantTx bool Enables redudant packet transmission if set to true. This option will enable the packets to be transmitted twice.
*
* ssdv_conf.quality int(0-7) Quality (quantization) of the JPEG algorithm. It can be set from 0 (low quality) to 7 (high quality). (Recommended: 4)
*
* ============================== The following options are needed if protocol == PROT_APRS_AFSK or protocol == PROT_APRS_2GFSK ===============================
*
* aprs_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the APRS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* aprs_conf.ssid int(0-15) APRS SSID (no SSID = 0)
* (default 0)
*
* aprs_conf.path string APRS digipeating path (default: no digipeating)
* (optional)
*
* aprs_conf.preamble int AFSK or 2GFSK preamble length (in ms). This value is required while its default is 0ms (and this would simply not work ;-) )
* (required)
*
* ============================================= The following options are needed if protocol == PROT_APRS_2GFSK ==============================================
*
* gfsk_conf.speed int 2GFSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*
* ============================================== The following options are needed if protocol == PROT_SSDV_2FSK ==============================================
*
* fsk_conf.bits int(7-8) Bits
* (required)
*
* fsk_conf.stopbits int(1-2) Stopbits (Important: DL-FlDigi can decode SSDV only using 2 stopbits!)
* (required)
*
* fsk_conf.predelay int Predelay (in ms). The receiver needs to settle on the frequency for a while. Therefore is switched on some seconds
* (default 0ms) before. By default its 0ms but that wouldnt work.
*
* fsk_conf.baud int Baudrate. Following values have been tested successfully: 50, 300, 600. (Recommended: 300 or 600)
* (required)
*
* fsk_conf.shift int Frequency shift of 2FSK
* (required)
*/
/*
* Log module configuration description
* ====================================
*
* This module activates log (track point) transmission over APRS (AFSK or 2GFSK). This module can be used to receive log points while the tracker has been out
* of range from the APRS network.
* Note: Track points are saved on the flash memory without activation of the Log module. They are just not sent out.
*
* power int(0-127) Defines the radio power level. It ranges from 0 (low) to 127 (high). This value is sent into the Si4464 register. The
* (required) real output power is operation voltage dependent. The operation voltage can be set by RUN_3V in config.h. If USB is
* activated the operation voltage is 3V regardsless to which value RUN_3V is set. Running the PCB at 3V following table
* applies:
* 20 dBm => 127
* 15 dBm => 40
* 10 dBm => 20
* 5 dBm => 12
* 0 dBm => 8
*
* protocol prot_t Possible Options:
* (required) - PROT_APRS_AFSK FM AFSK 1200baud transmission using the APRS protocol
* this option requires aprs_conf to be set
* - PROT_APRS_2GFSK 2GFSK transmission using the APRS protocol
* this option requires aprs_conf and gfsk_conf to be set
*
* frequency.type freq_type_t Defines the frequency type. This option will be FREQ_STATIC if not set.
* (default FREQ_STATIC) Possible Options:
* - FREQ_STATIC Static frequency taken from frequency.hz
* - FREQ_APRS_REGION Using the APRS region frequency. The tracker will change its frequency specificly to the region
* where it is located. e.g. 144.8MHz in Europe or 144.39MHz in the US. If the tracker doesnt know its
* position it takes its frequency from frequency.hz as default. Note that the tracker knows its
* position from its position log too. So it might use the last frequency which has been used before
* resetting it.
*
* frequency.hz int Frequency that this Module will transmit on (in Hz). The tracker can transmit in the 2m band. This value will be used
* (required) as default when frequency.type == FREQ_APRS_REGION and it doesnt know its position
*
* init_delay int Initial delay (in ms) before the module starts. This might be useful if you dont want to transmit so many APRS packets
* (default 0ms) at the same time on the APRS network. This option is optional. It will be 0ms if not set.
*
* trigger.type trigger_type_t Event at which this module is triggered to transmit. This option will be TRIG_ONCE if not set.
* (default TRIG_ONCE) Possible options:
* - TRIG_ONCE Trigger once and never again (e.g. transmit specific position packet only at startup)
* - TRIG_NEW_POINT Triggered when new track point available
* - TRIG_TIMEOUT Triggered by timeout (e.g. trasmit position every 120sec)
* this option requires trigger.timeout to be set
* - TRIG_CONTINUOUSLY Continue continuously (e.g. send new image once old image sent completely)
*
* trigger.timeout int Amount of seconds of module cycle (in seconds). This option is only neccessary if trigger.type == TRIG_TIMEOUT.
* (default 0s)
*
* aprs_conf.callsign string Your amateur radio callsign (this requires an amateur radio license). This callsign will be used in the APRS protocol.
* (required) You can transmit on the 70cm band without a license but the transmitter would need a 70cm LPF therefore.
*
* aprs_conf.ssid int(0-15) APRS SSID (no SSID = 0)
* (default 0)
*
* aprs_conf.path string APRS digipeating path (default: no digipeating)
* (optional)
*
* aprs_conf.preamble int AFSK or 2GFSK preamble length (in ms). This value is required while its default is 0ms (and this would simply not work ;-) )
* (required)
*
* ============================================= The following options are needed if protocol == PROT_APRS_2GFSK ==============================================
*
* gfsk_conf.speed int 2GFSK speed. Following values have been tested successfully: 9600, 19200.
* (required)
*/
// Put your configuration settings here
// Global variables
#include "config.h"
#include "aprs.h"
#include "debug.h"
module_conf_t config[7];
uint8_t ssdv_buffer[128*1024] __attribute__((aligned(32))); // Image buffer
systime_t track_cycle_time = S2ST(60); // Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each 60 seconds
systime_t log_cycle_time = S2ST(600); // Log cycle time in seconds (600 seconds)
bool keep_cam_switched_on = false; // Keep camera switched on and initialized after it has been switched on once, no configuration change is possible
uint16_t gps_on_vbat = 3000; // Battery voltage threshold at which GPS is switched on
uint16_t gps_off_vbat = 2500; // Battery voltage threshold at which GPS is switched off
void start_user_modules(void)
{
/*
* Use one of these example blocks by uncommenting the start_*_thread() line.
*/
/* -------------------------------------------------- POSITION TRANSMISSION -------------------------------------------------- */
// Module POSITION, APRS 2m AFSK
config[0].power = 127; // Power 20 dBm
config[0].protocol = PROT_APRS_AFSK; // Protocol APRS, modulation AFSK
config[0].frequency.type = FREQ_STATIC; // Dynamic frequency allocation
config[0].frequency.hz = 144390000; // Default frequency 144.800 MHz
config[0].init_delay = 0; // Module startup delay in msec
config[0].trigger.type = TRIG_NEW_POINT; // Trigger when new track point released
chsnprintf(config[0].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign
config[0].aprs_conf.ssid = 11; // APRS SSID
config[0].power = 127; // Transmission Power
config[0].protocol = PROT_APRS_AFSK; // Protocol APRS (AFSK)
config[0].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[0].frequency.hz = 144800000; // Default frequency 144.800 MHz
config[0].trigger.type = TRIG_NEW_POINT; // Transmit when tracking manager samples new tracking point
chsnprintf(config[0].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[0].aprs_conf.ssid = 12; // APRS SSID
config[0].aprs_conf.symbol = SYM_BALLOON; // APRS Symbol
chsnprintf(config[0].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[0].aprs_conf.preamble = 300; // APRS Preamble
config[0].aprs_conf.tel[0] = TEL_VBAT; // APRS Telemetry parameter 1
config[0].aprs_conf.tel[1] = TEL_PBAT; // APRS Telemetry parameter 2
config[0].aprs_conf.tel[2] = TEL_TEMP; // APRS Telemetry parameter 3
config[0].aprs_conf.tel[3] = TEL_PRESS; // APRS Telemetry parameter 4
config[0].aprs_conf.tel[4] = TEL_HUM; // APRS Telemetry parameter 5
config[0].aprs_conf.preamble = 300; // APRS Preamble (300ms)
config[0].aprs_conf.tel[0] = TEL_VBAT; // APRS Telemetry parameter 1: Battery voltage
config[0].aprs_conf.tel[1] = TEL_PBAT; // APRS Telemetry parameter 2: Battery charge/discharge power
config[0].aprs_conf.tel[2] = TEL_RBAT; // APRS Telemetry parameter 3: Battery impedance
config[0].aprs_conf.tel[3] = TEL_TEMP; // APRS Telemetry parameter 4: Temperature
config[0].aprs_conf.tel[4] = TEL_PRESS; // APRS Telemetry parameter 5: Airpressuse
config[0].aprs_conf.tel_enc = TRUE; // Transmit Telemetry encoding information activated
config[0].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
chsnprintf(config[0].aprs_conf.tel_comment, 30, "http://ssdv.habhub.org/DL7AD");// Telemetry comment
//start_position_thread(&config[0]);
// Module POSITION, APRS 2m 2GFSK
/*config[1].power = 127; // Power 10 dBm
config[1].protocol = PROT_APRS_2GFSK; // Protocol APRS, modulation 2GFSK
config[1].gfsk_conf.speed = 9600; // 2GFSK Speed
config[1].frequency.type = FREQ_STATIC; // Static frequency allocation
config[1].frequency.hz = 144860000; // Default frequency 144.860 MHz
config[1].init_delay = 0; // Module startup delay in msec
config[1].trigger.type = TRIG_NEW_POINT; // Trigger when new track point released
chsnprintf(config[1].aprs_conf.callsign, 7, "DL4MDW"); // APRS Callsign
config[1].aprs_conf.ssid = 11; // APRS SSID
config[1].aprs_conf.symbol = SYM_BALLOON; // APRS Symbol
chsnprintf(config[1].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[1].aprs_conf.preamble = 40; // APRS Preamble
config[0].aprs_conf.tel[0] = TEL_VBAT; // APRS Telemetry parameter 1
config[0].aprs_conf.tel[1] = TEL_VSOL; // APRS Telemetry parameter 2
config[0].aprs_conf.tel[2] = TEL_PRESS; // APRS Telemetry parameter 3
config[0].aprs_conf.tel[3] = TEL_TEMP; // APRS Telemetry parameter 4
config[0].aprs_conf.tel[4] = TEL_PBAT; // APRS Telemetry parameter 5
config[1].aprs_conf.tel_enc = TRUE; // Transmit Telemetry encoding information enabled
config[1].aprs_conf.tel_enc_cycle = 3600; // Transmit Telemetry encoding information every 3600sec
chsnprintf(config[1].aprs_conf.tel_comment, 18, "http://tkrahn.net");// Telemetry comment
start_position_thread(&config[1]);*/
// Module POSITION, UKHAS 2m 2FSK
/*config[2].power = 127; // Power 10 dBm
config[2].protocol = PROT_UKHAS_2FSK; // Protocol UKHAS, modulation 2FSK
config[2].frequency.type = FREQ_STATIC; // Static frequency allocation
config[2].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
config[2].init_delay = 2000; // Module startup delay in msec
config[2].trigger.type = TRIG_NEW_POINT; // Trigger when new track point released
config[2].fsk_conf.bits = 8; // 8bit
config[2].fsk_conf.stopbits = 2; // 2 Stopbits
config[2].fsk_conf.predelay = 1000; // Predelay in ms (continuos carrier before actual transmission)
config[2].fsk_conf.baud = 600; // Baudrate
config[2].fsk_conf.shift = 1000; // Frequency shift in Hz
chsnprintf(config[2].ukhas_conf.callsign, 6, "DK0TU"); // UKHAS Callsign
chsnprintf(config[2].ukhas_conf.format, 94, "<CALL>,<ID>,<TIME>,<LAT>,<LON>,<ALT>,<SATS>,<TTFF>,<VBAT>,<PBAT>,<PRESS>,<TEMP>,<HUM>"); // UKHAS Format
start_position_thread(&config[2]);*/
// Module IMAGE, APRS 2m AFSK low-duty cycle
config[3].power = 127; // Power 20 dBm
config[3].protocol = PROT_APRS_AFSK; // Protocol APRS SSDV, modulation AFSK
config[3].frequency.type = FREQ_STATIC; // Dynamic frequency allocation
config[3].frequency.hz = 144390000; // Transmission frequency 144.800 MHz
config[3].init_delay = 0; // Module startup delay in msec
//config[3].packet_spacing = 30000; // Packet spacing in ms
//config[3].sleep_conf.type = SLEEP_WHEN_ISOL_BELOW_THRES;
//config[3].sleep_conf.isol_thres = 3;
config[3].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
config[3].trigger.timeout = 10; // Timeout 10 sec
chsnprintf(config[3].aprs_conf.callsign, 7, "DL7AD"); // APRS Callsign
config[3].aprs_conf.ssid = 11; // APRS SSID
config[3].aprs_conf.preamble = 300; // APRS Preamble
chsnprintf(config[3].ssdv_conf.callsign, 7, "DL7AD"); // SSDV Callsign
config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[3].ssdv_conf.res = RES_QVGA; // Resolution VGA
config[3].ssdv_conf.redundantTx = true; // Transmit packets twice
//start_image_thread(&config[3]);
config[1].power = 127; // Transmission Power
config[1].protocol = PROT_UKHAS_2FSK; // Protocol UKHAS (2FSK)
config[1].frequency.type = FREQ_STATIC; // Static frequency allocation
config[1].frequency.hz = 144160000; // Transmission frequency 144.160 MHz
config[1].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
config[1].fsk_conf.bits = 8; // 8 bit
config[1].fsk_conf.stopbits = 2; // 2 stopbits
config[1].fsk_conf.predelay = 1000; // Preamble (1000ms)
config[1].fsk_conf.baud = 50; // Baudrate
config[1].fsk_conf.shift = 425; // Frequency shift in Hz
chsnprintf(config[1].ukhas_conf.callsign, 16, "DL7AD"); // UKHAS Callsign
chsnprintf(config[1].ukhas_conf.format, 150, "<CALL>,<ID>,<TIME>,<LAT>,<LON>,<ALT>,<SATS>,<TTFF>,<VBAT>,<PBAT>,<PRESS>,<TEMP>,<HUM>"); // UKHAS Format
//start_position_thread(&config[1]);
// Module POSITION, Morse 2m OOK
/*config[4].power = 127; // Power 10 dBm
config[4].protocol = PROT_MORSE; // Protocol Morse, modulation OOK
config[4].frequency.type = FREQ_STATIC; // Static frequency allocation
config[4].frequency.hz = 144857400; // Transmission frequency 144.8574 MHz
config[4].init_delay = 20000; // Module startup delay in msec
config[4].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
config[4].trigger.timeout = 1200; // Timeout 1200 sec
config[4].ook_conf.speed = 20; // Speed 20wpm
chsnprintf(config[4].morse_conf.callsign, 6, "DK0TU"); // Morse Callsign
chsnprintf(config[4].morse_conf.format, 43, "BALLOON <CALL> <LOC> <ALT>M WWW.TKRAHN.NET"); // Morse Format
start_position_thread(&config[4]);*/
config[2].power = 127; // Transmission Power
config[2].protocol = PROT_MORSE; // Protocol Morse (OOK)
config[2].frequency.type = FREQ_STATIC; // Static frequency allocation
config[2].frequency.hz = 144160000; // Transmission frequency 144.160 MHz
config[2].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 60 seconds)
config[2].trigger.timeout = 60; // Timeout 60 sec
config[2].ook_conf.speed = 20; // Speed 20 wpm
chsnprintf(config[2].morse_conf.callsign, 16, "DL7AD"); // Morse Callsign
chsnprintf(config[2].morse_conf.format, 50, "BALLOON <CALL> <LOC> <ALT>M"); // Morse Format
//start_position_thread(&config[2]);
/* ---------------------------------------------------- IMAGE TRANSMISSION --------------------------------------------------- */
// Module IMAGE, APRS 2m AFSK low-duty cycle
config[3].power = 127; // Transmission Power
config[3].protocol = PROT_APRS_AFSK; // Protocol APRS/SSDV (AFSK)
config[3].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[3].frequency.hz = 144800000; // Transmission frequency 144.800 MHz
config[3].packet_spacing = 20000; // Packet spacing in ms
config[3].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
chsnprintf(config[3].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[3].aprs_conf.ssid = 12; // APRS SSID
config[3].aprs_conf.preamble = 300; // APRS Preamble (300ms)
chsnprintf(config[3].ssdv_conf.callsign, 7, "DL7AD1"); // SSDV Callsign
config[3].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[3].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[3].ssdv_conf.res = RES_QVGA; // Resolution QVGA
config[3].ssdv_conf.redundantTx = true; // Redundant transmission (transmit packets twice)
config[3].ssdv_conf.quality = 4; // Image quality
//start_image_thread(&config[3]);
// Module IMAGE, APRS 2m 2GFSK
config[5].power = 127; // Power 20 dBm
config[5].protocol = PROT_APRS_2GFSK; // Protocol APRS SSDV, modulation 2GFSK
config[5].gfsk_conf.speed = 9600; // 2GFSK Speed
config[5].frequency.type = FREQ_STATIC; // Static frequency allocation
config[5].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
//config[5].init_delay = 60000; // Module startup delay in msec
config[5].sleep_conf.type = SLEEP_WHEN_VBAT_BELOW_THRES;
config[5].sleep_conf.vbat_thres = 4000;
config[5].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
config[5].trigger.timeout = 30; // Timeout 10 sec
chsnprintf(config[5].aprs_conf.callsign, 6, "DL7AD"); // APRS Callsign
config[5].aprs_conf.ssid = 12; // APRS SSID
config[5].aprs_conf.preamble = 100; // APRS Preamble
chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign
config[5].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[5].ssdv_conf.res = RES_VGA; // Resolution XGA
//config[5].ssdv_conf.redundantTx = true; // Transmit packets twice
start_image_thread(&config[5]);
config[4].power = 127; // Transmission Power
config[4].protocol = PROT_APRS_2GFSK; // Protocol APRS/SSDV (2GFSK)
config[4].gfsk_conf.speed = 9600; // 2GFSK Speed
config[4].frequency.type = FREQ_STATIC; // Static frequency allocation
config[4].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
config[4].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
chsnprintf(config[4].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[4].aprs_conf.ssid = 12; // APRS SSID
config[4].aprs_conf.preamble = 100; // APRS Preamble (100ms)
chsnprintf(config[4].ssdv_conf.callsign, 7, "DL7AD2"); // SSDV Callsign
config[4].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[4].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[4].ssdv_conf.res = RES_VGA; // Resolution VGA
config[4].ssdv_conf.quality = 4; // Image quality
start_image_thread(&config[4]);
// Module IMAGE, SSDV 2m 2FSK
/*config[6].power = 127; // Power 20 dBm
config[6].protocol = PROT_SSDV_2FSK; // Protocol SSDV, modulation 2FSK
config[6].frequency.type = FREQ_STATIC; // Static frequency allocation
config[6].frequency.hz = 144860000; // Transmission frequency 144.860 MHz
config[6].init_delay = 30000; // Module startup delay in msec
config[6].packet_spacing = 6500; // Packet spacing in ms
config[6].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
config[6].trigger.timeout = 1200; // Transmit every 900 sec
config[6].fsk_conf.bits = 8; // 8bit
config[6].fsk_conf.stopbits = 2; // 2 Stopbits
config[6].fsk_conf.predelay = 100; // Predelay in ms (continuos carrier before actual transmission)
config[6].fsk_conf.baud = 600; // Baudrate
config[6].fsk_conf.shift = 1000; // Frequency shift in Hz
chsnprintf(config[6].ssdv_conf.callsign, 6, "DK0TU"); // SSDV Callsign
config[6].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[6].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[6].ssdv_conf.res = RES_VGA; // Resolution VGA
start_image_thread(&config[6]);*/
config[5].power = 127; // Transmission Power
config[5].protocol = PROT_SSDV_2FSK; // Protocol SSDV (2FSK)
config[5].frequency.type = FREQ_STATIC; // Static frequency allocation
config[5].frequency.hz = 144160000; // Transmission frequency 144.160 MHz
config[5].trigger.type = TRIG_CONTINUOUSLY; // Transmit continuously
config[5].fsk_conf.bits = 8; // 8bit
config[5].fsk_conf.stopbits = 2; // 2 Stopbits
config[5].fsk_conf.predelay = 1000; // Preamble (1000ms)
config[5].fsk_conf.baud = 600; // Baudrate (600baud)
config[5].fsk_conf.shift = 1000; // Frequency shift (1000Hz)
chsnprintf(config[5].ssdv_conf.callsign, 7, "DL7AD3"); // SSDV Callsign
config[5].ssdv_conf.ram_buffer = ssdv_buffer; // Camera buffer
config[5].ssdv_conf.ram_size = sizeof(ssdv_buffer); // Buffer size
config[5].ssdv_conf.res = RES_QVGA; // Resolution QVGA
config[5].ssdv_conf.quality = 4; // Image quality
//start_image_thread(&config[5]);
/* ----------------------------------------------------- LOG TRANSMISSION ---------------------------------------------------- */
// Module LOG, APRS 2m AFSK
/*config[8].power = 127; // Power 20 dBm
config[8].protocol = PROT_APRS_AFSK; // Protocol APRS, modulation AFSK
config[8].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[8].frequency.hz = 144800000; // Default frequency 144.800 MHz
config[8].init_delay = 10000; // Module startup delay in msec
config[8].trigger.type = TRIG_TIMEOUT; // Trigger transmission on timeout (Periodic cycling)
config[8].trigger.timeout = 180; // Timeout 600 sec
chsnprintf(config[8].aprs_conf.callsign, 7, "DL4MDW"); // APRS Callsign
config[8].aprs_conf.ssid = 12; // APRS SSID
chsnprintf(config[8].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[8].aprs_conf.preamble = 300; // APRS Preamble
start_logging_thread(&config[8]);*/
config[6].power = 127; // Transmission Power
config[6].protocol = PROT_APRS_AFSK; // Protocol APRS (AFSK)
config[6].frequency.type = FREQ_APRS_REGION; // Dynamic frequency allocation
config[6].frequency.hz = 144800000; // Default frequency 144.800 MHz
config[6].init_delay = 60000; // Module startup delay (60 seconds)
config[6].trigger.type = TRIG_TIMEOUT; // Periodic cycling (every 180 seconds)
config[6].trigger.timeout = 180; // Timeout 180 sec
chsnprintf(config[6].aprs_conf.callsign, 16, "DL7AD"); // APRS Callsign
config[6].aprs_conf.ssid = 12; // APRS SSID
chsnprintf(config[6].aprs_conf.path, 16, "WIDE1-1"); // APRS Path
config[6].aprs_conf.preamble = 300; // APRS Preamble (300ms)
//start_logging_thread(&config[6]);
}

Wyświetl plik

@ -8,25 +8,26 @@
#include "radio.h"
#include "sleep.h"
#define TRACK_CYCLE_TIME 60 /* Tracking cycle (all peripheral data [airpressure, GPS, temperature, ...] is collected each x seconds */
#define LOG_CYCLE_TIME 30 /* Log cycle time in seconds */
#define LOG_FLASH_ADDR1 0x080C0000 /* Log flash memory address 1 */
#define LOG_FLASH_ADDR2 0x080E0000 /* Log flash memory address 2 */
#define LOG_SECTOR_SIZE 0x20000 /* Log flash memory size */
#define GPS_ON_VBAT 0 /* Battery voltage threshold at which GPS is switched on */
#define GPS_OFF_VBAT 0 /* Battery voltage threshold at which GPS is switched off */
#define TRACE_TIME TRUE /* Enables time tracing on debugging port */
#define TRACE_FILE TRUE /* Enables file and line tracing on debugging port */
#define TRACE_FILE FALSE /* Enables file and line tracing on debugging port */
#define RUN_3V TRUE /* Lets the tracker run a 3V otherwise 1.8V. 3V is needed to do 20dBm radio output power.
* With 1.8V only 15dBm can be done. Some serial-USB adapters also need a 3V IO level in
* order to work. However 3V takes a lot of power in idle. You can save energy using 1.8V. */
void start_user_modules(void);
extern module_conf_t config[9];
extern module_conf_t config[7];
extern systime_t track_cycle_time;
extern systime_t log_cycle_time;
extern bool keep_cam_switched_on;
extern uint16_t gps_on_vbat;
extern uint16_t gps_off_vbat;
#endif

Wyświetl plik

@ -73,21 +73,6 @@ extern bool debug_on_usb;
#define TRACE_TAB " "
#endif
#define TRACE_GPSFIX(fix) { \
TRACE_INFO("GPS > New GPS Fix\r\n"\
"%s GPS Time: %04d-%02d-%02d %02d:%02d:%02d\r\n" \
"%s Sats: %d (used for solution)\r\n" \
"%s Latitude: %d.%07ddeg\r\n" \
"%s Longitude: %d.%07ddeg\r\n" \
"%s Altitude: %d Meter", \
TRACE_TAB, (fix)->time.year, (fix)->time.month, (fix)->time.day, (fix)->time.hour, (fix)->time.minute, (fix)->time.second, \
TRACE_TAB, (fix)->num_svs, \
TRACE_TAB, (fix)->lat/10000000, ((fix)->lat > 0 ? 1:-1)*(fix)->lat%10000000, \
TRACE_TAB, (fix)->lon/10000000, ((fix)->lon > 0 ? 1:-1)*(fix)->lon%10000000, \
TRACE_TAB, (fix)->alt \
); \
}
#define TRACE_BIN(data, len) { \
chMtxLock(&trace_mtx); \
chprintf((BaseSequentialStream*)&SD3, "[%8d.%03d][DEBUG] ", chVTGetSystemTimeX()/CH_CFG_ST_FREQUENCY, (chVTGetSystemTimeX()*1000/CH_CFG_ST_FREQUENCY)%1000); \

Wyświetl plik

@ -1,836 +0,0 @@
/**
* This is the OV2640 driver
* I2C configuring concept has been taken from
* https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c
*/
/*
* ov2640 Camera Driver
*
* Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
*
* Based on ov772x, ov9640 drivers and previous non merged implementations.
*
* Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2006, OmniVision
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "ch.h"
#include "hal.h"
#include "ov2640.h"
#include "pi2c.h"
#include "board.h"
#include "defines.h"
#include "debug.h"
#include <string.h>
#define OV2640_I2C_ADR 0x30
#define VAL_SET(x, mask, rshift, lshift) \
((((x) >> rshift) & mask) << lshift)
/*
* DSP registers
* register offset for BANK_SEL == BANK_SEL_DSP
*/
#define R_BYPASS 0x05 /* Bypass DSP */
#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
#define QS 0x44 /* Quantization Scale Factor */
#define CTRLI 0x50
#define CTRLI_LP_DP 0x80
#define CTRLI_ROUND 0x40
#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define XOFFL 0x53 /* OFFSET_X[7:0] */
#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define YOFFL 0x54 /* OFFSET_Y[7:0] */
#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define VHYX 0x55 /* Offset and size completion */
#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
#define DPRP 0x56
#define TEST 0x57 /* Horizontal size completion */
#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
#define CTRL2 0x86 /* DSP Module enable 2 */
#define CTRL2_DCW_EN 0x20
#define CTRL2_SDE_EN 0x10
#define CTRL2_UV_ADJ_EN 0x08
#define CTRL2_UV_AVG_EN 0x04
#define CTRL2_CMX_EN 0x01
#define CTRL3 0x87 /* DSP Module enable 3 */
#define CTRL3_BPC_EN 0x80
#define CTRL3_WPC_EN 0x40
#define SIZEL 0x8C /* Image Size Completion */
#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define CTRL0 0xC2 /* DSP Module enable 0 */
#define CTRL0_AEC_EN 0x80
#define CTRL0_AEC_SEL 0x40
#define CTRL0_STAT_SEL 0x20
#define CTRL0_VFIRST 0x10
#define CTRL0_YUV422 0x08
#define CTRL0_YUV_EN 0x04
#define CTRL0_RGB_EN 0x02
#define CTRL0_RAW_EN 0x01
#define CTRL1 0xC3 /* DSP Module enable 1 */
#define CTRL1_CIP 0x80
#define CTRL1_DMY 0x40
#define CTRL1_RAW_GMA 0x20
#define CTRL1_DG 0x10
#define CTRL1_AWB 0x08
#define CTRL1_AWB_GAIN 0x04
#define CTRL1_LENC 0x02
#define CTRL1_PRE 0x01
#define R_DVP_SP 0xD3 /* DVP output speed control */
#define R_DVP_SP_AUTO_MODE 0x80
#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
* = sysclk (48)/(2*[6:0]) (RAW);*/
#define IMAGE_MODE 0xDA /* Image Output Format Select */
#define IMAGE_MODE_Y8_DVP_EN 0x40
#define IMAGE_MODE_JPEG_EN 0x10
#define IMAGE_MODE_YUV422 0x00
#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
#define IMAGE_MODE_RGB565 0x08
#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output
* mode (0 for HREF is same as sensor) */
#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP
* 1: Low byte first UYVY (C2[4] =0)
* VYUY (C2[4] =1)
* 0: High byte first YUYV (C2[4]=0)
* YVYU (C2[4] = 1) */
#define RESET 0xE0 /* Reset */
#define RESET_MICROC 0x40
#define RESET_SCCB 0x20
#define RESET_JPEG 0x10
#define RESET_DVP 0x04
#define RESET_IPU 0x02
#define RESET_CIF 0x01
#define REGED 0xED /* Register ED */
#define REGED_CLK_OUT_DIS 0x10
#define MS_SP 0xF0 /* SCCB Master Speed */
#define SS_ID 0xF7 /* SCCB Slave ID */
#define SS_CTRL 0xF8 /* SCCB Slave Control */
#define SS_CTRL_ADD_AUTO_INC 0x20
#define SS_CTRL_EN 0x08
#define SS_CTRL_DELAY_CLK 0x04
#define SS_CTRL_ACC_EN 0x02
#define SS_CTRL_SEN_PASS_THR 0x01
#define MC_BIST 0xF9 /* Microcontroller misc register */
#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
#define MC_BIST_BOOT_ROM_SEL 0x40
#define MC_BIST_12KB_SEL 0x20
#define MC_BIST_12KB_MASK 0x30
#define MC_BIST_512KB_SEL 0x08
#define MC_BIST_512KB_MASK 0x0C
#define MC_BIST_BUSY_BIT_R 0x02
#define MC_BIST_MC_RES_ONE_SH_W 0x02
#define MC_BIST_LAUNCH 0x01
#define BANK_SEL 0xFF /* Register Bank Select */
#define BANK_SEL_DSP 0x00
#define BANK_SEL_SENS 0x01
/*
* Sensor registers
* register offset for BANK_SEL == BANK_SEL_SENS
*/
#define GAIN 0x00 /* AGC - Gain control gain setting */
#define COM1 0x03 /* Common control 1 */
#define COM1_1_DUMMY_FR 0x40
#define COM1_3_DUMMY_FR 0x80
#define COM1_7_DUMMY_FR 0xC0
#define COM1_VWIN_LSB_UXGA 0x0F
#define COM1_VWIN_LSB_SVGA 0x0A
#define COM1_VWIN_LSB_CIF 0x06
#define REG04 0x04 /* Register 04 */
#define REG04_DEF 0x20 /* Always set */
#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
#define REG04_VREF_EN 0x10
#define REG04_HREF_EN 0x08
#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
#define COM2 0x09 /* Common control 2 */
#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
/* Output drive capability */
#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
#define PID 0x0A /* Product ID Number MSB */
#define VER 0x0B /* Product ID Number LSB */
#define COM3 0x0C /* Common control 3 */
#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
#define COM3_BAND_AUTO 0x02 /* Auto Banding */
#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the
* snapshot sequence*/
#define AEC 0x10 /* AEC[9:2] Exposure Value */
#define CLKRC 0x11 /* Internal clock */
#define CLKRC_EN 0x80
#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
#define COM7 0x12 /* Common control 7 */
#define COM7_SRST 0x80 /* Initiates system reset. All registers are
* set to factory default values after which
* the chip resumes normal operation */
#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
#define COM7_RES_SVGA 0x40 /* SVGA */
#define COM7_RES_CIF 0x20 /* CIF */
#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
#define COM8 0x13 /* Common control 8 */
#define COM8_DEF 0xC0 /* Banding filter ON/OFF */
#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
#define COM9 0x14 /* Common control 9
* Automatic gain ceiling - maximum AGC value [7:5]*/
#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
#define COM10 0x15 /* Common control 10 */
#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of
* PCLK (user can latch data at the next
* falling edge of PCLK).
* 0 otherwise. */
#define COM10_HREF_INV 0x08 /* Invert HREF polarity:
* HREF negative for valid data*/
#define COM10_VSYNC_INV 0x02 /* Invert VSYNC polarity */
#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
#define HEND 0x18 /* Horizontal Window end MSB 8 bit */
#define VSTART 0x19 /* Vertical Window start MSB 8 bit */
#define VEND 0x1A /* Vertical Window end MSB 8 bit */
#define MIDH 0x1C /* Manufacturer ID byte - high */
#define MIDL 0x1D /* Manufacturer ID byte - low */
#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
#define VV 0x26 /* AGC/AEC Fast mode operating region */
#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
#define REG2A 0x2A /* Dummy pixel insert MSB */
#define FRARL 0x2B /* Dummy pixel insert LSB */
#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
#define YAVG 0x2F /* Y/G Channel Average value */
#define REG32 0x32 /* Common Control 32 */
#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
#define ARCOM2 0x34 /* Zoom: Horizontal start point */
#define REG45 0x45 /* Register 45 */
#define FLL 0x46 /* Frame Length Adjustment LSBs */
#define FLH 0x47 /* Frame Length Adjustment MSBs */
#define COM19 0x48 /* Zoom: Vertical start point */
#define ZOOMS 0x49 /* Zoom: Vertical start point */
#define COM22 0x4B /* Flash light control */
#define COM25 0x4E /* For Banding operations */
#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
#define MANUFACTURER_ID 0x7FA2
#define PID_OV2640 0x2626
#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
struct regval_list {
uint8_t reg;
uint8_t val;
};
/* Supported resolutions */
enum ov2640_width {
W_QCIF = 176,
W_QVGA = 320,
W_CIF = 352,
W_VGA = 640,
W_SVGA = 800,
W_XGA = 1024,
W_SXGA = 1280,
W_UXGA = 1600,
};
enum ov2640_height {
H_QCIF = 144,
H_QVGA = 240,
H_CIF = 288,
H_VGA = 480,
H_SVGA = 600,
H_XGA = 768,
H_SXGA = 1024,
H_UXGA = 1200,
};
struct ov2640_win_size {
char *name;
enum ov2640_width width;
enum ov2640_height height;
const struct regval_list *regs;
};
/*
* Registers settings. Most of them are undocumented. Some documentation is
* is available in the OV2640 datasheet, the OV2640 hardware app notes and
* the OV2640 software app notes documents.
*/
#define ENDMARKER { 0xff, 0xff }
static const struct regval_list ov2640_init_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ 0x2c, 0xff },
{ 0x2e, 0xdf },
{ BANK_SEL, BANK_SEL_SENS },
{ 0x3c, 0x32 },
{ CLKRC, CLKRC_DIV_SET(8) },
{ COM2, COM2_OCAP_Nx_SET(3) },
{ REG04, REG04_DEF | REG04_HREF_EN },
{ COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN },
//~ { AEC, 0x00 },
{ COM9, COM9_AGC_GAIN_8x | 0x08},
//{ COM10, COM10_PCLK_RISE },
{ 0x2c, 0x0c },
{ 0x33, 0x78 },
{ 0x3a, 0x33 },
{ 0x3b, 0xfb },
{ 0x3e, 0x00 },
{ 0x43, 0x11 },
{ 0x16, 0x10 },
{ 0x39, 0x02 },
{ 0x35, 0x88 },
{ 0x22, 0x0a },
{ 0x37, 0x40 },
{ 0x23, 0x00 },
{ ARCOM2, 0xa0 },
{ 0x06, 0x02 },
{ 0x06, 0x88 },
{ 0x07, 0xc0 },
{ 0x0d, 0xb7 },
{ 0x0e, 0x01 },
{ 0x4c, 0x00 },
{ 0x4a, 0x81 },
{ 0x21, 0x99 },
{ AEW, 0x40 },
{ AEB, 0x38 },
{ VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) },
{ 0x5c, 0x00 },
{ 0x63, 0x00 },
{ FLL, 0x22 },
{ COM3, 0x38 | COM3_BAND_AUTO },
{ REG5D, 0x55 },
{ REG5E, 0x7d },
{ REG5F, 0x7d },
{ REG60, 0x55 },
{ HISTO_LOW, 0x70 },
{ HISTO_HIGH, 0x80 },
{ 0x7c, 0x05 },
{ 0x20, 0x80 },
{ 0x28, 0x30 },
{ 0x6c, 0x00 },
{ 0x6d, 0x80 },
{ 0x6e, 0x00 },
{ 0x70, 0x02 },
{ 0x71, 0x94 },
{ 0x73, 0xc1 },
{ 0x3d, 0x34 },
{ COM7, COM7_RES_UXGA | COM7_ZOOM_EN },
{ 0x5a, 0x57 },
{ BD50, 0xbb },
{ BD60, 0x9c },
{ BANK_SEL, BANK_SEL_DSP },
{ 0xe5, 0x7f },
{ MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
{ 0x41, 0x24 },
{ RESET, RESET_JPEG | RESET_DVP },
{ 0x76, 0xff },
{ 0x33, 0xa0 },
{ 0x42, 0x20 },
{ 0x43, 0x18 },
{ 0x4c, 0x00 },
{ CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
{ 0x88, 0x3f },
{ 0xd7, 0x03 },
{ 0xd9, 0x10 },
{ R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
{ 0xc8, 0x08 },
{ 0xc9, 0x80 },
{ BPADDR, 0x00 },
{ BPDATA, 0x00 },
{ BPADDR, 0x03 },
{ BPDATA, 0x48 },
{ BPDATA, 0x48 },
{ BPADDR, 0x08 },
{ BPDATA, 0x20 },
{ BPDATA, 0x10 },
{ BPDATA, 0x0e },
{ 0x90, 0x00 },
{ 0x91, 0x0e },
{ 0x91, 0x1a },
{ 0x91, 0x31 },
{ 0x91, 0x5a },
{ 0x91, 0x69 },
{ 0x91, 0x75 },
{ 0x91, 0x7e },
{ 0x91, 0x88 },
{ 0x91, 0x8f },
{ 0x91, 0x96 },
{ 0x91, 0xa3 },
{ 0x91, 0xaf },
{ 0x91, 0xc4 },
{ 0x91, 0xd7 },
{ 0x91, 0xe8 },
{ 0x91, 0x20 },
{ 0x92, 0x00 },
{ 0x93, 0x06 },
{ 0x93, 0xe3 },
{ 0x93, 0x03 },
{ 0x93, 0x03 },
{ 0x93, 0x00 },
{ 0x93, 0x02 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x96, 0x00 },
{ 0x97, 0x08 },
{ 0x97, 0x19 },
{ 0x97, 0x02 },
{ 0x97, 0x0c },
{ 0x97, 0x24 },
{ 0x97, 0x30 },
{ 0x97, 0x28 },
{ 0x97, 0x26 },
{ 0x97, 0x02 },
{ 0x97, 0x98 },
{ 0x97, 0x80 },
{ 0x97, 0x00 },
{ 0x97, 0x00 },
{ 0xa4, 0x00 },
{ 0xa8, 0x00 },
{ 0xc5, 0x11 },
{ 0xc6, 0x51 },
{ 0xbf, 0x80 },
{ 0xc7, 0x10 }, /* white balance */
{ 0xb6, 0x66 },
{ 0xb8, 0xA5 },
{ 0xb7, 0x64 },
{ 0xb9, 0x7C },
{ 0xb3, 0xaf },
{ 0xb4, 0x97 },
{ 0xb5, 0xFF },
{ 0xb0, 0xC5 },
{ 0xb1, 0x94 },
{ 0xb2, 0x0f },
{ 0xc4, 0x5c },
{ 0xa6, 0x00 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x1b },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x19 },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x19 },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0x7f, 0x00 },
{ 0xe5, 0x1f },
{ 0xe1, 0x77 },
{ 0xdd, 0x7f },
{ QS, 0x0C },
{ CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN },
ENDMARKER,
};
/*
* Register settings for window size
* The preamble, setup the internal DSP to input an UXGA (1600x1200) image.
* Then the different zooming configurations will setup the output image size.
*/
static const struct regval_list ov2640_size_change_preamble_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ RESET, RESET_DVP },
{ HSIZE8, HSIZE8_SET(W_UXGA) },
{ VSIZE8, VSIZE8_SET(H_UXGA) },
{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
{ HSIZE, HSIZE_SET(W_UXGA) },
{ VSIZE, VSIZE_SET(H_UXGA) },
{ XOFFL, XOFFL_SET(0) },
{ YOFFL, YOFFL_SET(0) },
{ VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
{ TEST, TEST_HSIZE_SET(W_UXGA) },
ENDMARKER,
};
#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
{ CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \
CTRLI_H_DIV_SET(h_div)}, \
{ ZMOW, ZMOW_OUTW_SET(x) }, \
{ ZMOH, ZMOH_OUTH_SET(y) }, \
{ ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \
{ R_DVP_SP, pclk_div }, \
{ RESET, 0x00}
static const struct regval_list ov2640_qcif_regs[] = {
PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
ENDMARKER,
};
static const struct regval_list ov2640_qvga_regs[] = {
PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
ENDMARKER,
};
static const struct regval_list ov2640_cif_regs[] = {
PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
ENDMARKER,
};
static const struct regval_list ov2640_vga_regs[] = {
PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
ENDMARKER,
};
static const struct regval_list ov2640_svga_regs[] = {
PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
ENDMARKER,
};
static const struct regval_list ov2640_xga_regs[] = {
PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
{ CTRLI, 0x00},
ENDMARKER,
};
static const struct regval_list ov2640_sxga_regs[] = {
PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
{ CTRLI, 0x00},
{ R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
ENDMARKER,
};
static const struct regval_list ov2640_uxga_regs[] = {
PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
{ CTRLI, 0x00},
{ R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
ENDMARKER,
};
#define OV2640_SIZE(n, w, h, r) \
{.name = n, .width = w , .height = h, .regs = r }
static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
};
/*
* Register settings for pixel formats
*/
static const struct regval_list ov2640_format_change_preamble_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_yuyv_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_YUV422 },
{ 0xd7, 0x03 },
{ 0x33, 0xa0 },
{ 0xe5, 0x1f },
{ 0xe1, 0x67 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_uyvy_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
{ 0xd7, 0x01 },
{ 0x33, 0xa0 },
{ 0xe1, 0x67 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_rgb565_be_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_RGB565 },
{ 0xd7, 0x03 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_rgb565_le_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
{ 0xd7, 0x03 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_jpeg_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ 0xe0, 0x14 },
{ 0xe1, 0x77 },
{ 0xe5, 0x1f },
{ 0xd7, 0x03 },
{ IMAGE_MODE, IMAGE_MODE_JPEG_EN },
{ 0xe0, 0x00 },
{ BANK_SEL, BANK_SEL_SENS },
{ 0x04, 0x08 },
ENDMARKER,
};
ssdv_conf_t *ov2640_conf;
uint32_t size;
/**
* Captures an image from the camera.
*/
bool OV2640_Snapshot2RAM(void)
{
// Capture enable
TRACE_INFO("CAM > Capture image");
OV2640_Capture();
return true;
}
bool OV2640_BufferOverflow(void)
{
return ov2640_conf->ram_buffer[0] != 0xFF || ov2640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header
}
uint32_t OV2640_getBuffer(uint8_t** buffer) {
*buffer = ov2640_conf->ram_buffer;
return ov2640_conf->size_sampled;
}
bool OV2640_Capture(void)
{
TRACE_INFO("CAM > Start capture");
while(palReadLine(LINE_CAM_VSYNC));
while(!palReadLine(LINE_CAM_VSYNC));
uint8_t gpioc;
uint8_t gpioa;
ov2640_conf->size_sampled = 0;
while(true)
{
do {
gpioc = GPIOC->IDR & 0x7;
} while((gpioc & 0x1) != 0x1); // Wait for PCLK to rise
gpioa = GPIOA->IDR;
switch(gpioc) {
case 0x3:
break;
case 0x7:
ov2640_conf->ram_buffer[ov2640_conf->size_sampled++] = gpioa;
break;
default:
return true;
}
// Wait for falling edge
while(GPIOC->IDR & 0x1);
}
}
/**
* Initializes GPIO (for DCMI)
* The high speed clock supports communication by I2C (XCLK = 16MHz)
*/
void OV2640_InitGPIO(void)
{
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(1));
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_XCLK, PAL_MODE_ALTERNATE(0));
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL);
palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL);
}
void OV2640_TransmitConfig(void)
{
// Set to page 1
I2C_write8(OV2640_I2C_ADR, 0xff, 0x01);
I2C_write8(OV2640_I2C_ADR, 0x12, 0x80);
chThdSleepMilliseconds(50);
/* Write selected arrays to the camera to initialize it and set the
* desired output format. */
for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val);
for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val);
switch(ov2640_conf->res) {
case RES_QCIF:
for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val);
break;
case RES_QVGA:
for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val);
break;
case RES_VGA:
for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val);
break;
case RES_XGA:
for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val);
break;
case RES_UXGA:
for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val);
break;
default: // Default QVGA
for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val);
}
for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val);
for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val);
for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++)
I2C_write8(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val);
}
void OV2640_init(ssdv_conf_t *config) {
ov2640_conf = config;
// Clearing buffer
uint32_t i;
for(i=0; i<ov2640_conf->ram_size; i++)
ov2640_conf->ram_buffer[i] = 0;
TRACE_INFO("CAM > Init pins");
OV2640_InitGPIO();
// Power on OV2640
TRACE_INFO("CAM > Switch on");
palSetLine(LINE_CAM_EN); // Switch on camera
palSetLine(LINE_CAM_RESET); // Toggle reset
// Send settings to OV2640
TRACE_INFO("CAM > Transmit config to camera");
OV2640_TransmitConfig();
chThdSleepMilliseconds(3000);
}
void OV2640_deinit(void) {
// Power off OV2640
TRACE_INFO("CAM > Switch off");
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_XCLK, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_EN, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT);
}
bool OV2640_isAvailable(void)
{
// Configure pins
OV2640_InitGPIO();
// Switch on camera
palSetLine(LINE_CAM_EN); // Switch on camera
palSetLine(LINE_CAM_RESET); // Toggle reset
chThdSleepMilliseconds(100);
uint16_t val;
bool ret;
if(I2C_read16(OV2640_I2C_ADR, 0x0A, &val))
ret = val == PID_OV2640;
else
ret = false;
palClearLine(LINE_CAM_EN); // Switch off camera
palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT); // CAM_RESET
return ret;
}

Wyświetl plik

@ -1,22 +0,0 @@
/**
* This is the OV2640 driver
*/
#ifndef __OV2640_H__
#define __OV2640_H__
#include "ch.h"
#include "hal.h"
#include "types.h"
bool OV2640_Snapshot2RAM(void);
bool OV2640_Capture(void);
void OV2640_InitGPIO(void);
uint32_t OV2640_getBuffer(uint8_t** buffer);
bool OV2640_BufferOverflow(void);
void OV2640_TransmitConfig(void);
void OV2640_init(ssdv_conf_t *config);
void OV2640_deinit(void);
bool OV2640_isAvailable(void);
#endif

Wyświetl plik

@ -1,958 +0,0 @@
/**
* This is the OV2640 driver
* I2C configuring concept has been taken from
* https://github.com/iqyx/ov2640-stm32/blob/master/F4discovery/main.c
*/
/*
* ov2640 Camera Driver
*
* Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
*
* Based on ov772x, ov9640 drivers and previous non merged implementations.
*
* Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2006, OmniVision
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "ch.h"
#include "hal.h"
#include "ov2640.h"
#include "pi2c.h"
#include "board.h"
#include "defines.h"
#include "debug.h"
#include <string.h>
#define OV2640_I2C_ADR 0x30
#define VAL_SET(x, mask, rshift, lshift) \
((((x) >> rshift) & mask) << lshift)
/*
* DSP registers
* register offset for BANK_SEL == BANK_SEL_DSP
*/
#define R_BYPASS 0x05 /* Bypass DSP */
#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
#define QS 0x44 /* Quantization Scale Factor */
#define CTRLI 0x50
#define CTRLI_LP_DP 0x80
#define CTRLI_ROUND 0x40
#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define XOFFL 0x53 /* OFFSET_X[7:0] */
#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define YOFFL 0x54 /* OFFSET_Y[7:0] */
#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
#define VHYX 0x55 /* Offset and size completion */
#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
#define DPRP 0x56
#define TEST 0x57 /* Horizontal size completion */
#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
#define CTRL2 0x86 /* DSP Module enable 2 */
#define CTRL2_DCW_EN 0x20
#define CTRL2_SDE_EN 0x10
#define CTRL2_UV_ADJ_EN 0x08
#define CTRL2_UV_AVG_EN 0x04
#define CTRL2_CMX_EN 0x01
#define CTRL3 0x87 /* DSP Module enable 3 */
#define CTRL3_BPC_EN 0x80
#define CTRL3_WPC_EN 0x40
#define SIZEL 0x8C /* Image Size Completion */
#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
#define CTRL0 0xC2 /* DSP Module enable 0 */
#define CTRL0_AEC_EN 0x80
#define CTRL0_AEC_SEL 0x40
#define CTRL0_STAT_SEL 0x20
#define CTRL0_VFIRST 0x10
#define CTRL0_YUV422 0x08
#define CTRL0_YUV_EN 0x04
#define CTRL0_RGB_EN 0x02
#define CTRL0_RAW_EN 0x01
#define CTRL1 0xC3 /* DSP Module enable 1 */
#define CTRL1_CIP 0x80
#define CTRL1_DMY 0x40
#define CTRL1_RAW_GMA 0x20
#define CTRL1_DG 0x10
#define CTRL1_AWB 0x08
#define CTRL1_AWB_GAIN 0x04
#define CTRL1_LENC 0x02
#define CTRL1_PRE 0x01
#define R_DVP_SP 0xD3 /* DVP output speed control */
#define R_DVP_SP_AUTO_MODE 0x80
#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
* = sysclk (48)/(2*[6:0]) (RAW);*/
#define IMAGE_MODE 0xDA /* Image Output Format Select */
#define IMAGE_MODE_Y8_DVP_EN 0x40
#define IMAGE_MODE_JPEG_EN 0x10
#define IMAGE_MODE_YUV422 0x00
#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
#define IMAGE_MODE_RGB565 0x08
#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output
* mode (0 for HREF is same as sensor) */
#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP
* 1: Low byte first UYVY (C2[4] =0)
* VYUY (C2[4] =1)
* 0: High byte first YUYV (C2[4]=0)
* YVYU (C2[4] = 1) */
#define RESET 0xE0 /* Reset */
#define RESET_MICROC 0x40
#define RESET_SCCB 0x20
#define RESET_JPEG 0x10
#define RESET_DVP 0x04
#define RESET_IPU 0x02
#define RESET_CIF 0x01
#define REGED 0xED /* Register ED */
#define REGED_CLK_OUT_DIS 0x10
#define MS_SP 0xF0 /* SCCB Master Speed */
#define SS_ID 0xF7 /* SCCB Slave ID */
#define SS_CTRL 0xF8 /* SCCB Slave Control */
#define SS_CTRL_ADD_AUTO_INC 0x20
#define SS_CTRL_EN 0x08
#define SS_CTRL_DELAY_CLK 0x04
#define SS_CTRL_ACC_EN 0x02
#define SS_CTRL_SEN_PASS_THR 0x01
#define MC_BIST 0xF9 /* Microcontroller misc register */
#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
#define MC_BIST_BOOT_ROM_SEL 0x40
#define MC_BIST_12KB_SEL 0x20
#define MC_BIST_12KB_MASK 0x30
#define MC_BIST_512KB_SEL 0x08
#define MC_BIST_512KB_MASK 0x0C
#define MC_BIST_BUSY_BIT_R 0x02
#define MC_BIST_MC_RES_ONE_SH_W 0x02
#define MC_BIST_LAUNCH 0x01
#define BANK_SEL 0xFF /* Register Bank Select */
#define BANK_SEL_DSP 0x00
#define BANK_SEL_SENS 0x01
/*
* Sensor registers
* register offset for BANK_SEL == BANK_SEL_SENS
*/
#define GAIN 0x00 /* AGC - Gain control gain setting */
#define COM1 0x03 /* Common control 1 */
#define COM1_1_DUMMY_FR 0x40
#define COM1_3_DUMMY_FR 0x80
#define COM1_7_DUMMY_FR 0xC0
#define COM1_VWIN_LSB_UXGA 0x0F
#define COM1_VWIN_LSB_SVGA 0x0A
#define COM1_VWIN_LSB_CIF 0x06
#define REG04 0x04 /* Register 04 */
#define REG04_DEF 0x20 /* Always set */
#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
#define REG04_VREF_EN 0x10
#define REG04_HREF_EN 0x08
#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
#define COM2 0x09 /* Common control 2 */
#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
/* Output drive capability */
#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
#define PID 0x0A /* Product ID Number MSB */
#define VER 0x0B /* Product ID Number LSB */
#define COM3 0x0C /* Common control 3 */
#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
#define COM3_BAND_AUTO 0x02 /* Auto Banding */
#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the
* snapshot sequence*/
#define AEC 0x10 /* AEC[9:2] Exposure Value */
#define CLKRC 0x11 /* Internal clock */
#define CLKRC_EN 0x80
#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
#define COM7 0x12 /* Common control 7 */
#define COM7_SRST 0x80 /* Initiates system reset. All registers are
* set to factory default values after which
* the chip resumes normal operation */
#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
#define COM7_RES_SVGA 0x40 /* SVGA */
#define COM7_RES_CIF 0x20 /* CIF */
#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
#define COM8 0x13 /* Common control 8 */
#define COM8_DEF 0xC0 /* Banding filter ON/OFF */
#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
#define COM9 0x14 /* Common control 9
* Automatic gain ceiling - maximum AGC value [7:5]*/
#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
#define COM10 0x15 /* Common control 10 */
#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of
* PCLK (user can latch data at the next
* falling edge of PCLK).
* 0 otherwise. */
#define COM10_HREF_INV 0x08 /* Invert HREF polarity:
* HREF negative for valid data*/
#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */
#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
#define HEND 0x18 /* Horizontal Window end MSB 8 bit */
#define VSTART 0x19 /* Vertical Window start MSB 8 bit */
#define VEND 0x1A /* Vertical Window end MSB 8 bit */
#define MIDH 0x1C /* Manufacturer ID byte - high */
#define MIDL 0x1D /* Manufacturer ID byte - low */
#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
#define VV 0x26 /* AGC/AEC Fast mode operating region */
#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
#define REG2A 0x2A /* Dummy pixel insert MSB */
#define FRARL 0x2B /* Dummy pixel insert LSB */
#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
#define YAVG 0x2F /* Y/G Channel Average value */
#define REG32 0x32 /* Common Control 32 */
#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
#define ARCOM2 0x34 /* Zoom: Horizontal start point */
#define REG45 0x45 /* Register 45 */
#define FLL 0x46 /* Frame Length Adjustment LSBs */
#define FLH 0x47 /* Frame Length Adjustment MSBs */
#define COM19 0x48 /* Zoom: Vertical start point */
#define ZOOMS 0x49 /* Zoom: Vertical start point */
#define COM22 0x4B /* Flash light control */
#define COM25 0x4E /* For Banding operations */
#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
#define MANUFACTURER_ID 0x7FA2
#define PID_OV2640 0x2626
#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
struct regval_list {
uint8_t reg;
uint8_t val;
};
/* Supported resolutions */
enum ov2640_width {
W_QCIF = 176,
W_QVGA = 320,
W_CIF = 352,
W_VGA = 640,
W_SVGA = 800,
W_XGA = 1024,
W_SXGA = 1280,
W_UXGA = 1600,
};
enum ov2640_height {
H_QCIF = 144,
H_QVGA = 240,
H_CIF = 288,
H_VGA = 480,
H_SVGA = 600,
H_XGA = 768,
H_SXGA = 1024,
H_UXGA = 1200,
};
struct ov2640_win_size {
char *name;
enum ov2640_width width;
enum ov2640_height height;
const struct regval_list *regs;
};
/*
* Registers settings. Most of them are undocumented. Some documentation is
* is available in the OV2640 datasheet, the OV2640 hardware app notes and
* the OV2640 software app notes documents.
*/
#define ENDMARKER { 0xff, 0xff }
static const struct regval_list ov2640_init_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ 0x2c, 0xff },
{ 0x2e, 0xdf },
{ BANK_SEL, BANK_SEL_SENS },
{ 0x3c, 0x32 },
{ CLKRC, CLKRC_DIV_SET(2) },
{ COM2, COM2_OCAP_Nx_SET(3) },
{ REG04, REG04_DEF | REG04_HREF_EN },
{ COM8, COM8_DEF | COM8_AGC_EN | COM8_AEC_EN | COM8_BNDF_EN },
//~ { AEC, 0x00 },
{ COM9, COM9_AGC_GAIN_8x | 0x08},
{ 0x2c, 0x0c },
{ 0x33, 0x78 },
{ 0x3a, 0x33 },
{ 0x3b, 0xfb },
{ 0x3e, 0x00 },
{ 0x43, 0x11 },
{ 0x16, 0x10 },
{ 0x39, 0x02 },
{ 0x35, 0x88 },
{ 0x22, 0x0a },
{ 0x37, 0x40 },
{ 0x23, 0x00 },
{ ARCOM2, 0xa0 },
{ 0x06, 0x02 },
{ 0x06, 0x88 },
{ 0x07, 0xc0 },
{ 0x0d, 0xb7 },
{ 0x0e, 0x01 },
{ 0x4c, 0x00 },
{ 0x4a, 0x81 },
{ 0x21, 0x99 },
{ AEW, 0x40 },
{ AEB, 0x38 },
{ VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) },
{ 0x5c, 0x00 },
{ 0x63, 0x00 },
{ FLL, 0x22 },
{ COM3, 0x38 | COM3_BAND_AUTO },
{ REG5D, 0x55 },
{ REG5E, 0x7d },
{ REG5F, 0x7d },
{ REG60, 0x55 },
{ HISTO_LOW, 0x70 },
{ HISTO_HIGH, 0x80 },
{ 0x7c, 0x05 },
{ 0x20, 0x80 },
{ 0x28, 0x30 },
{ 0x6c, 0x00 },
{ 0x6d, 0x80 },
{ 0x6e, 0x00 },
{ 0x70, 0x02 },
{ 0x71, 0x94 },
{ 0x73, 0xc1 },
{ 0x3d, 0x34 },
{ COM7, COM7_RES_UXGA | COM7_ZOOM_EN },
{ 0x5a, 0x57 },
{ BD50, 0xbb },
{ BD60, 0x9c },
{ BANK_SEL, BANK_SEL_DSP },
{ 0xe5, 0x7f },
{ MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
{ 0x41, 0x24 },
{ RESET, RESET_JPEG | RESET_DVP },
{ 0x76, 0xff },
{ 0x33, 0xa0 },
{ 0x42, 0x20 },
{ 0x43, 0x18 },
{ 0x4c, 0x00 },
{ CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
{ 0x88, 0x3f },
{ 0xd7, 0x03 },
{ 0xd9, 0x10 },
{ R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
{ 0xc8, 0x08 },
{ 0xc9, 0x80 },
{ BPADDR, 0x00 },
{ BPDATA, 0x00 },
{ BPADDR, 0x03 },
{ BPDATA, 0x48 },
{ BPDATA, 0x48 },
{ BPADDR, 0x08 },
{ BPDATA, 0x20 },
{ BPDATA, 0x10 },
{ BPDATA, 0x0e },
{ 0x90, 0x00 },
{ 0x91, 0x0e },
{ 0x91, 0x1a },
{ 0x91, 0x31 },
{ 0x91, 0x5a },
{ 0x91, 0x69 },
{ 0x91, 0x75 },
{ 0x91, 0x7e },
{ 0x91, 0x88 },
{ 0x91, 0x8f },
{ 0x91, 0x96 },
{ 0x91, 0xa3 },
{ 0x91, 0xaf },
{ 0x91, 0xc4 },
{ 0x91, 0xd7 },
{ 0x91, 0xe8 },
{ 0x91, 0x20 },
{ 0x92, 0x00 },
{ 0x93, 0x06 },
{ 0x93, 0xe3 },
{ 0x93, 0x03 },
{ 0x93, 0x03 },
{ 0x93, 0x00 },
{ 0x93, 0x02 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x93, 0x00 },
{ 0x96, 0x00 },
{ 0x97, 0x08 },
{ 0x97, 0x19 },
{ 0x97, 0x02 },
{ 0x97, 0x0c },
{ 0x97, 0x24 },
{ 0x97, 0x30 },
{ 0x97, 0x28 },
{ 0x97, 0x26 },
{ 0x97, 0x02 },
{ 0x97, 0x98 },
{ 0x97, 0x80 },
{ 0x97, 0x00 },
{ 0x97, 0x00 },
{ 0xa4, 0x00 },
{ 0xa8, 0x00 },
{ 0xc5, 0x11 },
{ 0xc6, 0x51 },
{ 0xbf, 0x80 },
{ 0xc7, 0x10 }, /* white balance */
{ 0xb6, 0x66 },
{ 0xb8, 0xA5 },
{ 0xb7, 0x64 },
{ 0xb9, 0x7C },
{ 0xb3, 0xaf },
{ 0xb4, 0x97 },
{ 0xb5, 0xFF },
{ 0xb0, 0xC5 },
{ 0xb1, 0x94 },
{ 0xb2, 0x0f },
{ 0xc4, 0x5c },
{ 0xa6, 0x00 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x1b },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x19 },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0xa7, 0x20 },
{ 0xa7, 0xd8 },
{ 0xa7, 0x19 },
{ 0xa7, 0x31 },
{ 0xa7, 0x00 },
{ 0xa7, 0x18 },
{ 0x7f, 0x00 },
{ 0xe5, 0x1f },
{ 0xe1, 0x77 },
{ 0xdd, 0x7f },
{ QS, 0x0C },
{ CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN },
ENDMARKER,
};
/*
* Register settings for window size
* The preamble, setup the internal DSP to input an UXGA (1600x1200) image.
* Then the different zooming configurations will setup the output image size.
*/
static const struct regval_list ov2640_size_change_preamble_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ RESET, RESET_DVP },
{ HSIZE8, HSIZE8_SET(W_UXGA) },
{ VSIZE8, VSIZE8_SET(H_UXGA) },
{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
{ HSIZE, HSIZE_SET(W_UXGA) },
{ VSIZE, VSIZE_SET(H_UXGA) },
{ XOFFL, XOFFL_SET(0) },
{ YOFFL, YOFFL_SET(0) },
{ VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
{ TEST, TEST_HSIZE_SET(W_UXGA) },
ENDMARKER,
};
#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
{ CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \
CTRLI_H_DIV_SET(h_div)}, \
{ ZMOW, ZMOW_OUTW_SET(x) }, \
{ ZMOH, ZMOH_OUTH_SET(y) }, \
{ ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \
{ R_DVP_SP, pclk_div }, \
{ RESET, 0x00}
static const struct regval_list ov2640_qcif_regs[] = {
PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
ENDMARKER,
};
static const struct regval_list ov2640_qvga_regs[] = {
PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
ENDMARKER,
};
static const struct regval_list ov2640_cif_regs[] = {
PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
ENDMARKER,
};
static const struct regval_list ov2640_vga_regs[] = {
PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
ENDMARKER,
};
static const struct regval_list ov2640_svga_regs[] = {
PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
ENDMARKER,
};
static const struct regval_list ov2640_xga_regs[] = {
PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
{ CTRLI, 0x00},
ENDMARKER,
};
static const struct regval_list ov2640_sxga_regs[] = {
PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
{ CTRLI, 0x00},
{ R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
ENDMARKER,
};
static const struct regval_list ov2640_uxga_regs[] = {
PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
{ CTRLI, 0x00},
{ R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
ENDMARKER,
};
#define OV2640_SIZE(n, w, h, r) \
{.name = n, .width = w , .height = h, .regs = r }
static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
};
/*
* Register settings for pixel formats
*/
static const struct regval_list ov2640_format_change_preamble_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_yuyv_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_YUV422 },
{ 0xd7, 0x03 },
{ 0x33, 0xa0 },
{ 0xe5, 0x1f },
{ 0xe1, 0x67 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_uyvy_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
{ 0xd7, 0x01 },
{ 0x33, 0xa0 },
{ 0xe1, 0x67 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_rgb565_be_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_RGB565 },
{ 0xd7, 0x03 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_rgb565_le_regs[] = {
{ IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
{ 0xd7, 0x03 },
{ RESET, 0x00 },
{ R_BYPASS, R_BYPASS_USE_DSP },
ENDMARKER,
};
static const struct regval_list ov2640_jpeg_regs[] = {
{ BANK_SEL, BANK_SEL_DSP },
{ 0xe0, 0x14 },
{ 0xe1, 0x77 },
{ 0xe5, 0x1f },
{ 0xd7, 0x03 },
{ IMAGE_MODE, IMAGE_MODE_JPEG_EN },
{ 0xe0, 0x00 },
{ BANK_SEL, BANK_SEL_SENS },
{ 0x04, 0x08 },
ENDMARKER,
};
ssdv_conf_t *ov2640_conf;
uint32_t size;
/**
* Captures an image from the camera.
*/
bool OV2640_Snapshot2RAM(void)
{
// Capture enable
TRACE_INFO("CAM > Capture image");
OV2640_Capture();
return true;
}
bool OV2640_BufferOverflow(void)
{
return ov2640_conf->ram_buffer[0] != 0xFF || ov2640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header
}
uint32_t OV2640_getBuffer(uint8_t** buffer) {
*buffer = ov2640_conf->ram_buffer;
return ov2640_conf->size_sampled;
}
const stm32_dma_stream_t *dmastp;
int32_t dma_start(void) {
dmaStreamEnable(dmastp);
return 0;
}
int32_t dma_stop(void) {
dmaStreamDisable(dmastp);
return 0;
}
static void dma_interrupt(void *p, uint32_t flags) {
(void)p;
if ((flags & STM32_DMA_ISR_HTIF) != 0) { // I think we dont need this
}
if ((flags & STM32_DMA_ISR_TCIF) != 0) { // I think that stands for "memory filled completly"
palSetLine(LINE_IO_LED1);
dma_stop(); // Stop DMA
}
}
// This is the vector for EXTI2 (stolen from hal_ext_lld_isr.c)
CH_IRQ_HANDLER(Vector60) {
CH_IRQ_PROLOGUE();
uint8_t gpioc = GPIOC->IDR;
// HREF handling
if(gpioc & 0x4) {
// HREF rising edge, start capturing data on pixel clock
TIM5->DIER |= TIM_DIER_TDE;
} else {
// HREF falling edge, stop capturing
TIM5->DIER &= ~TIM_DIER_TDE;
}
EXTI->PR |= EXTI_PR_PR2;
CH_IRQ_EPILOGUE();
}
// This is the vector for EXTI1 (stolen from hal_ext_lld_isr.c)
CH_IRQ_HANDLER(Vector5C) {
CH_IRQ_PROLOGUE();
uint8_t gpioc = GPIOC->IDR;
// VSYNC handling
if(gpioc & 0x2) {
// VSYNC rising edge
// Start capturing image even if HREF is still low. We do this
// because the timers need some clocks to initialize. Therefore
// we will capture some bytes additionally in the beginning which
// we will remove later
dma_start();
TIM5->DIER |= TIM_DIER_TDE;
palClearLine(LINE_IO_LED1); // Indicate that picture will be captured
} else {
// VSYNC falling edge - end if JPEG frame. Stop the DMA, disable
// capture LED and signal the semaphore (data can be processed).
TIM5->DIER &= ~TIM_DIER_TDE;
dma_stop();
palSetLine(LINE_IO_LED1); // Indicate that picture is captured completly
}
EXTI->PR |= EXTI_PR_PR1;
CH_IRQ_EPILOGUE();
}
void OV2640_Capture(void)
{
// Setup DMA
dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(1, 2)); // TIM5_CH1 DMA1 Stream 2
uint32_t dmamode = STM32_DMA_CR_CHSEL(6) | // Channel 6
STM32_DMA_CR_PL(2) |
STM32_DMA_CR_DIR_P2M |
STM32_DMA_CR_MSIZE_BYTE |
STM32_DMA_CR_PSIZE_BYTE |
STM32_DMA_CR_MINC |
STM32_DMA_CR_DMEIE |
STM32_DMA_CR_TEIE |
STM32_DMA_CR_CIRC |
STM32_DMA_CR_TCIE |
STM32_DMA_CR_HTIE;
dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL);
dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here
dmaStreamSetMemory0(dmastp, ov2640_conf->ram_buffer); // Thats the buffer address
dmaStreamSetTransactionSize(dmastp, ov2640_conf->ram_size); // Thats the buffer size
dmaStreamSetMode(dmastp, dmamode); // Setup DMA
// Setup timer for PLCK
rccResetLPTIM1();
rccEnableLPTIM1(FALSE);
LPTIM1->CFGR = (LPTIM_CFGR_CKSEL | LPTIM_CFGR_CKPOL_1);
LPTIM1->OR |= LPTIM_OR_TIM5_ITR1_RMP;
rccResetTIM5();
rccEnableTIM5(FALSE);
TIM5->SMCR = TIM_SMCR_TS_0; // Select ITR1 as trigger
TIM5->SMCR |= TIM_SMCR_SMS_2; // Set timer in reset mode
TIM5->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1); // IC1 is mapped to TRC
//TIM5->DIER |= TIM_DIER_TDE; // Enable DMA on trigger request.
//TIM5->DIER |= TIM_DIER_TIE; // Enable interrupt on trigger request
TIM5->CR1 |= TIM_CR1_CEN; // Enable the timer
//nvicEnableVector(TIM5_IRQn, 7); // Enable interrupt
LPTIM1->CR |= LPTIM_CR_ENABLE;
LPTIM1->CMP = 1;
LPTIM1->ARR = 2;
LPTIM1->CR |= LPTIM_CR_CNTSTRT;
// Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF)
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC | SYSCFG_EXTICR1_EXTI2_PC;
EXTI->IMR = EXTI_IMR_MR1 | EXTI_IMR_MR2; // Activate interrupt for chan1 (=>PC1) and chan2 (=>PC2)
EXTI->RTSR = EXTI_RTSR_TR1 | EXTI_RTSR_TR2; // Listen on rising edge
EXTI->FTSR = EXTI_FTSR_TR1 | EXTI_FTSR_TR2; // Listen on falling edge too
nvicEnableVector(EXTI1_IRQn, 1); // Enable interrupt
nvicEnableVector(EXTI2_IRQn, 1); // Enable interrupt
while(true) { // Have a look for some bytes in memory for testing if capturing works
TRACE_DEBUG("CAM > BLA %d %d %d %d %d", TIM5->CNT, ov2640_conf->ram_buffer[0], ov2640_conf->ram_buffer[1]
, ov2640_conf->ram_buffer[1024], ov2640_conf->ram_buffer[2048]);
chThdSleepMilliseconds(1);
}
TRACE_INFO("CAM > Stop capture");
}
/**
* Initializes GPIO (for DCMI)
* The high speed clock supports communication by I2C (XCLK = 16MHz)
*/
void OV2640_InitGPIO(void)
{
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_ALTERNATE(1));
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_XCLK, PAL_MODE_ALTERNATE(0));
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT | PAL_STM32_OSPEED_HIGHEST);
palSetLineMode(LINE_CAM_EN, PAL_MODE_OUTPUT_PUSHPULL);
palSetLineMode(LINE_CAM_RESET, PAL_MODE_OUTPUT_PUSHPULL);
}
void OV2640_TransmitConfig(void)
{
// Set to page 1
I2C_write8_locked(OV2640_I2C_ADR, 0xff, 0x01);
I2C_write8_locked(OV2640_I2C_ADR, 0x12, 0x80);
chThdSleepMilliseconds(50);
/* Write selected arrays to the camera to initialize it and set the
* desired output format. */
for(uint32_t i=0; (ov2640_init_regs[i].reg != 0xff) || (ov2640_init_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_init_regs[i].reg, ov2640_init_regs[i].val);
for(uint32_t i=0; (ov2640_size_change_preamble_regs[i].reg != 0xff) || (ov2640_size_change_preamble_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_size_change_preamble_regs[i].reg, ov2640_size_change_preamble_regs[i].val);
switch(ov2640_conf->res) {
case RES_QCIF:
for(uint32_t i=0; (ov2640_qcif_regs[i].reg != 0xff) || (ov2640_qcif_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_qcif_regs[i].reg, ov2640_qcif_regs[i].val);
break;
case RES_QVGA:
for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val);
break;
case RES_VGA:
for(uint32_t i=0; (ov2640_vga_regs[i].reg != 0xff) || (ov2640_vga_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_vga_regs[i].reg, ov2640_vga_regs[i].val);
break;
case RES_XGA:
for(uint32_t i=0; (ov2640_xga_regs[i].reg != 0xff) || (ov2640_xga_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_xga_regs[i].reg, ov2640_xga_regs[i].val);
break;
case RES_UXGA:
for(uint32_t i=0; (ov2640_uxga_regs[i].reg != 0xff) || (ov2640_uxga_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_uxga_regs[i].reg, ov2640_uxga_regs[i].val);
break;
default: // Default QVGA
for(uint32_t i=0; (ov2640_qvga_regs[i].reg != 0xff) || (ov2640_qvga_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_qvga_regs[i].reg, ov2640_qvga_regs[i].val);
}
for(uint32_t i=0; (ov2640_format_change_preamble_regs[i].reg != 0xff) || (ov2640_format_change_preamble_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_format_change_preamble_regs[i].reg, ov2640_format_change_preamble_regs[i].val);
for(uint32_t i=0; (ov2640_yuyv_regs[i].reg != 0xff) || (ov2640_yuyv_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_yuyv_regs[i].reg, ov2640_yuyv_regs[i].val);
for(uint32_t i=0; (ov2640_jpeg_regs[i].reg != 0xff) || (ov2640_jpeg_regs[i].val != 0xff); i++)
I2C_write8_locked(OV2640_I2C_ADR, ov2640_jpeg_regs[i].reg, ov2640_jpeg_regs[i].val);
}
void OV2640_init(ssdv_conf_t *config) {
ov2640_conf = config;
// Take I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus)
I2C_lock();
// Clearing buffer
uint32_t i;
for(i=0; i<ov2640_conf->ram_size; i++)
ov2640_conf->ram_buffer[i] = 0;
TRACE_INFO("CAM > Init pins");
OV2640_InitGPIO();
// Power on OV2640
TRACE_INFO("CAM > Switch on");
palSetLine(LINE_CAM_EN); // Switch on camera
palSetLine(LINE_CAM_RESET); // Toggle reset
// Send settings to OV2640
TRACE_INFO("CAM > Transmit config to camera");
OV2640_TransmitConfig();
chThdSleepMilliseconds(3000);
}
void OV2640_deinit(void) {
// Power off OV2640
TRACE_INFO("CAM > Switch off");
palSetLineMode(LINE_CAM_HREF, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_PCLK, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_VSYNC, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_XCLK, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D2, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D3, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D4, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D5, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D6, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D7, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D8, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_D9, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_EN, PAL_MODE_INPUT);
palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT);
// Release I2C (due to silicon bug of OV2640, it interferes if byte 0x30 transmitted on I2C bus)
I2C_unlock();
}
bool OV2640_isAvailable(void)
{
I2C_lock();
// Configure pins
OV2640_InitGPIO();
// Switch on camera
palSetLine(LINE_CAM_EN); // Switch on camera
palSetLine(LINE_CAM_RESET); // Toggle reset
chThdSleepMilliseconds(100);
uint16_t val;
bool ret;
if(I2C_read16_locked(OV2640_I2C_ADR, 0x0A, &val))
ret = val == PID_OV2640;
else
ret = false;
palClearLine(LINE_CAM_EN); // Switch off camera
palSetLineMode(LINE_CAM_RESET, PAL_MODE_INPUT); // CAM_RESET
I2C_unlock();
return ret;
}

Wyświetl plik

@ -10,6 +10,7 @@
#include "board.h"
#include "defines.h"
#include "debug.h"
#include "ssdv.h"
#include <string.h>
#define OV5640_I2C_ADR 0x3C
@ -21,7 +22,7 @@ struct regval_list {
static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] =
{
{ 0x4740, 0x24 },
{ 0x4740, 0x04 },
{ 0x4050, 0x6e },
{ 0x4051, 0x8f },
@ -31,7 +32,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] =
{ 0x3017, 0x7f },
{ 0x3018, 0xff },
{ 0x302c, 0x02 },
{ 0x3108, 0x01 },
{ 0x3108, 0x31 },
{ 0x3630, 0x2e },//2e
{ 0x3632, 0xe2 },
{ 0x3633, 0x23 },//23
@ -69,7 +70,7 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] =
{ 0x3c0a, 0x9c },
{ 0x3c0b, 0x40 },
{ 0x3820, 0x41 },
{ 0x3820, 0x46 },
{ 0x3821, 0x01 }, //07
//windows setup
@ -93,9 +94,9 @@ static const struct regval_list OV5640YUV_Sensor_Dvp_Init[] =
{ 0x3815, 0x31 },
{ 0x3034, 0x1a },
{ 0x3035, 0x41 }, //15fps
{ 0x3035, 0x11 }, //15fps
{ 0x3036, 0x46 },
{ 0x3037, 0x13 },
{ 0x3037, 0x14 },
{ 0x3038, 0x00 },
{ 0x3039, 0x00 },
@ -327,10 +328,10 @@ static const struct regval_list ov5640_vga_preview[] =
{
// YUV VGA 30fps, night mode 5fps
// Input Clock = 24Mhz, PCLK = 56MHz
{ 0x3035, 0x41 }, // PLL
{ 0x3035, 0x11 }, // PLL
{ 0x3036, 0x46 }, // PLL
{ 0x3c07, 0x08 }, // light meter 1 threshold [7:0]
{ 0x3820, 0x41 }, // Sensor flip off, ISP flip on
{ 0x3820, 0x46 }, // Sensor flip off, ISP flip on
{ 0x3821, 0x01 }, // Sensor mirror on, ISP mirror on, H binning on
{ 0x3814, 0x31 }, // X INC
{ 0x3815, 0x31 }, // Y INC
@ -382,7 +383,7 @@ static const struct regval_list ov5640_vga_preview[] =
static const struct regval_list OV5640_RGB_QVGA[] =
{
{0x3008, 0x02},
{0x3035, 0x41},
{0x3035, 0x11},
{0x4740, 0x21},
{0x4300, 0x61},
{0x3808, 0x01},
@ -396,8 +397,8 @@ static const struct regval_list OV5640_RGB_QVGA[] =
//2592x1944 QSXGA
static const struct regval_list OV5640_JPEG_QSXGA[] =
{
{0x3820 ,0x40},
{0x3821 ,0x26},
{0x3820 ,0x46},
{0x3821 ,0x20},
{0x3814 ,0x11},
{0x3815 ,0x11},
{0x3803 ,0x00},
@ -429,7 +430,7 @@ static const struct regval_list OV5640_JPEG_QSXGA[] =
{0x3824 ,0x04},
{0x5001 ,0x83},
{0x3036 ,0x69},
{0x3035 ,0x41},
{0x3035 ,0x11},
{0x4005 ,0x1A},
{0xffff, 0xff},
};
@ -468,63 +469,94 @@ static const struct regval_list OV5640_5MP_JPEG[] =
//320x240 QVGA
static const struct regval_list OV5640_QSXGA2QVGA[] =
{
{0x3800 ,0x00},
{0x3801 ,0x00},
{0x3802 ,0x00},
{0x3803 ,0x00},
{0x3804 ,0xA },
{0x3805 ,0x3f},
{0x3806 ,0x7 },
{0x3807 ,0x9f},
{0x3808 ,0x1 },
{0x3809 ,0x40},
{0x380a ,0x0 },
{0x380b ,0xf0},
{0x380c ,0xc },
{0x380d ,0x80},
{0x380e ,0x7 },
{0x380f ,0xd0},
{0x5001 ,0xa3},
{0x5680 ,0x0 },
{0x5681 ,0x0 },
{0x5682 ,0xA },
{0x5683 ,0x20},
{0x5684 ,0x0 },
{0x5685 ,0x0 },
{0x5686 ,0x7 },
{0x5687 ,0x98},
{0xffff, 0xff},
{0x3800 ,0x00},
{0x3801 ,0x00},
{0x3802 ,0x00},
{0x3803 ,0x00},
{0x3804 ,0xA },
{0x3805 ,0x3f},
{0x3806 ,0x7 },
{0x3807 ,0x9f},
{0x3808 ,0x1 },
{0x3809 ,0x40},
{0x380a ,0x0 },
{0x380b ,0xf0},
{0x380c ,0xc },
{0x380d ,0x80},
{0x380e ,0x7 },
{0x380f ,0xd0},
{0x5001 ,0xa3},
{0x5680 ,0x0 },
{0x5681 ,0x0 },
{0x5682 ,0xA },
{0x5683 ,0x20},
{0x5684 ,0x0 },
{0x5685 ,0x0 },
{0x5686 ,0x7 },
{0x5687 ,0x98},
{0xffff, 0xff},
};
//320x240 QQVGA
static const struct regval_list OV5640_QSXGA2QQVGA[] =
{
{0x3800 ,0x00},
{0x3801 ,0x00},
{0x3802 ,0x00},
{0x3803 ,0x00},
{0x3804 ,0xA },
{0x3805 ,0x3f},
{0x3806 ,0x7 },
{0x3807 ,0x9f},
{0x3808 ,0x0 },
{0x3809 ,0xA0},
{0x380a ,0x0 },
{0x380b ,0x70},
{0x380c ,0xc },
{0x380d ,0x80},
{0x380e ,0x7 },
{0x380f ,0xd0},
{0x5001 ,0xa3},
{0x5680 ,0x0 },
{0x5681 ,0x0 },
{0x5682 ,0xA },
{0x5683 ,0x20},
{0x5684 ,0x0 },
{0x5685 ,0x0 },
{0x5686 ,0x7 },
{0x5687 ,0x98},
{0xffff, 0xff},
};
//640x480 VGA
static const struct regval_list OV5640_QSXGA2VGA[] =
{
{0x3800 ,0x00},
{0x3801 ,0x00},
{0x3802 ,0x00},
{0x3803 ,0x00},
{0x3804 ,0xA },
{0x3805 ,0x3f},
{0x3806 ,0x7 },
{0x3807 ,0x9f},
{0x3808 ,0x2 },
{0x3809 ,0x80},
{0x380a ,0x1 },
{0x380b ,0xe0},
{0x380c ,0xc },
{0x380d ,0x80},
{0x380e ,0x7 },
{0x380f ,0xd0},
{0x5001 ,0xa3},
{0x5680 ,0x0 },
{0x5681 ,0x0 },
{0x5682 ,0xA },
{0x3800 ,0x00},
{0x3801 ,0x00},
{0x3802 ,0x00},
{0x3803 ,0x00},
{0x3804 ,0xA },
{0x3805 ,0x3f},
{0x3806 ,0x7 },
{0x3807 ,0x9f},
{0x3808 ,0x2 },
{0x3809 ,0x80},
{0x380a ,0x1 },
{0x380b ,0xe0},
{0x380c ,0xc },
{0x380d ,0x80},
{0x380e ,0x7 },
{0x380f ,0xd0},
{0x5001 ,0xa3},
{0x5680 ,0x0 },
{0x5681 ,0x0 },
{0x5682 ,0xA },
{0x5683 ,0x20},
{0x5684 ,0x0 },
{0x5685 ,0x0 },
{0x5686 ,0x7 },
{0x5687 ,0x98},
{0xffff, 0xff},
{0x5685 ,0x0 },
{0x5686 ,0x7 },
{0x5687 ,0x98},
{0xffff, 0xff},
};
//800x480 WVGA
@ -723,29 +755,116 @@ static const struct regval_list OV5640_QSXGA2XGA[] =
};
static ssdv_conf_t *ov5640_conf;
/* TODO: Implement a state machine instead of multiple flags. */
static bool LptimRdy;
static bool image_finished;
static bool capture_finished;
static bool vsync;
static bool dma_error;
static uint32_t dma_flags;
static uint32_t oldSpeed;
static uint32_t oldWS;
/* Increase AHB clock to 48 MHz */
void set48MHz(void)
{
oldSpeed = RCC->CFGR & RCC_CFGR_HPRE_Msk;
oldWS = FLASH->ACR & FLASH_ACR_LATENCY_Msk;
uint32_t new = (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | FLASH_ACR_LATENCY_3WS;
FLASH->ACR = new;
while(FLASH->ACR != new);
new = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV1;
RCC->CFGR = new;
while(RCC->CFGR != new);
}
void set6MHz(void)
{
// Reduce AHB clock to 6 MHz
uint32_t new = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | oldSpeed;
RCC->CFGR = new;
while(RCC->CFGR != new);
new = (FLASH->ACR & ~FLASH_ACR_LATENCY_Msk) | oldWS;
FLASH->ACR = new;
while(FLASH->ACR != new);
}
/**
* Analyzes the image for JPEG errors. Returns true if the image is error free.
*/
static bool analyze_image(uint8_t *image, uint32_t image_len)
{
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE];
uint8_t *b;
uint32_t bi = 0;
uint8_t c = SSDV_OK;
ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, "", 0, 7);
ssdv_enc_set_buffer(&ssdv, pkt);
while(true) // FIXME: I get caught in these loops occasionally and never return
{
while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
{
b = &image[bi];
uint8_t r = bi < image_len-128 ? 128 : image_len - bi;
bi += r;
if(r <= 0)
{
TRACE_ERROR("CAM > Error in image");
return false;
}
ssdv_enc_feed(&ssdv, b, r);
}
if(c == SSDV_EOI) // End of image
return true;
if(c != SSDV_OK) // Error in JPEG image
{
TRACE_ERROR("CAM > Error in image");
return false;
}
chThdSleepMilliseconds(5);
}
}
bool OV5640_BufferOverflow(void)
{
return ov5640_conf->size_sampled == ov5640_conf->ram_size - 1; // If SRAM was used completly its most likley that and overflow has occured (TODO: This is not 100% accurate)
}
/**
* Captures an image from the camera.
*/
bool OV5640_Snapshot2RAM(void)
{
// Capture enable
TRACE_INFO("CAM > Capture image");
OV5640_Capture();
// Capture image until we get a good image (max 10 tries)
uint8_t cntr = 10;
bool status;
do {
TRACE_INFO("CAM > Capture image");
status = OV5640_Capture();
TRACE_INFO("CAM > Capture finished");
ov5640_conf->size_sampled = ov5640_conf->ram_size - 1;
while(!ov5640_conf->ram_buffer[ov5640_conf->size_sampled] && ov5640_conf->size_sampled > 0)
ov5640_conf->size_sampled--;
TRACE_INFO("CAM > Image size: %d bytes", ov5640_conf->size_sampled);
} while((!analyze_image(ov5640_conf->ram_buffer, ov5640_conf->ram_size) || !status) && cntr--);
return true;
}
bool OV5640_BufferOverflow(void)
{
return ov5640_conf->ram_buffer[0] != 0xFF || ov5640_conf->ram_buffer[1] != 0xD8; // Check for JPEG SOI header
}
uint32_t OV5640_getBuffer(uint8_t** buffer) {
*buffer = ov5640_conf->ram_buffer;
return ov5640_conf->size_sampled;
@ -753,75 +872,164 @@ uint32_t OV5640_getBuffer(uint8_t** buffer) {
const stm32_dma_stream_t *dmastp;
#if OV5640_USE_DMA_DBM == TRUE
uint16_t dma_index;
uint16_t dma_buffers;
#define DMA_SEGMENT_SIZE 1024
#define DMA_FIFO_BURST_ALIGN 32
#if !defined(dmaStreamGetCurrentTarget)
/**
* @brief Get DMA stream current target.
* @note This function can be invoked in both ISR or thread context.
* @pre The stream must have been allocated using @p dmaStreamAllocate().
* @post After use the stream can be released using @p dmaStreamRelease().
*
* @param[in] dmastp pointer to a stm32_dma_stream_t structure
* @return Current target index
*
* @special
*/
#define dmaStreamGetCurrentTarget(dmastp) \
((uint8_t)(((dmastp)->stream->CR >> DMA_SxCR_CT_Pos) & 1U))
#endif /* !defined(dmaStreamGetCurrentTarget) */
#endif /* OV5640_USE_DMA_DBM == TRUE */
inline int32_t dma_start(void) {
/* Clear any pending inerrupts. */
/* Clear any pending interrupts. */
dmaStreamClearInterrupt(dmastp);
dmaStreamEnable(dmastp);
return 0;
dmaStreamEnable(dmastp);
return 0;
}
inline int32_t dma_stop(void) {
/*
* Stop DMA release stream and return count remaining.
* Note that any DMA FIFO transfer will complete.
* The Chibios DMAV2 driver waits for EN to clear before proceeding.
*/
inline uint16_t dma_stop(void) {
dmaStreamDisable(dmastp);
uint16_t transfer = dmaStreamGetTransactionSize(dmastp);
dmaStreamRelease(dmastp);
return 0;
return transfer;
}
#if OV5640_USE_DMA_DBM == TRUE
static void dma_interrupt(void *p, uint32_t flags) {
(void)p;
/* No parameter passed. */
(void)p;
if ((flags & STM32_DMA_ISR_HTIF) != 0) {
/* Deprecate - Nothing really to do at half way point. */
return;
}
if ((flags & STM32_DMA_ISR_TCIF) != 0) {
/* Disable VYSNC edge interrupts. */
nvicDisableVector(EXTI1_IRQn);
image_finished = true;
dma_flags = flags;
if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF)) {
/*
* DMA transfer error or FIFO error.
* See 9.34.19 of RM0430.
*
* Disable timer DMA request and flag fault.
*/
TIM1->DIER &= ~TIM_DIER_TDE;
dma_error = true;
dmaStreamClearInterrupt(dmastp);
return;
}
/*
* Stop PCLK from LPTIM1 and disable TIM1 DMA trigger.
* Stop and release DMA channel.
* Either DMA count full or VSNC traling edge can terminate frame capture
*/
TIM1->DIER &= ~TIM_DIER_TDE;
LPTIM1->CR &= ~LPTIM_CR_CNTSTRT;
dma_stop();
return;
}
/*
* TODO: Anything else is an error.
* Maybe set an error flag?
*/
if (flags & STM32_DMA_ISR_HTIF) {
/*
* Half transfer complete.
* Check if DMA is writing to the last buffer.
*/
if (dma_index == (dma_buffers - 1)) {
/*
* This is the last buffer so we have to terminate DMA.
* The DBM switch is done in h/w.
* DMA could write beyond total buffer if not stopped.
*
* Since this is the last DMA buffer this is treated as an error.
* The DMA should normally be terminated by VSYNC before last buffer.
* Stop DMA and TIM DMA trigger and flag error.
*/
TIM1->DIER &= ~TIM_DIER_TDE;
dma_error = true;
dmaStreamClearInterrupt(dmastp);
return;
}
/*
* Else Safe to allow buffer to fill.
* DMA DBM will switch buffers in h/w when this one is full.
* Just clear the interrupt and wait for TCIF.
*/
dmaStreamClearInterrupt(dmastp);
return;
}
if (flags & STM32_DMA_ISR_TCIF) {
/*
* Full buffer transfer complete.
* Update non-active memory address register.
* DMA will use new address at h/w DBM switch.
*/
if (dmaStreamGetCurrentTarget(dmastp) == 1) {
dmaStreamSetMemory0(dmastp, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]);
} else {
dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[++dma_index * DMA_SEGMENT_SIZE]);
}
dmaStreamClearInterrupt(dmastp);
return;
}
}
/*
* The TIM1 interrupt handler (to be deprecated - not used).
*/
/* Not defined by Chibios. */
#define STM32_TIM1_TRG_HANDLER VectorA8
#define STM32_TIM1_UP_NUMBER 25
#else
OSAL_IRQ_HANDLER(STM32_TIM1_TRG_HANDLER) {
static void dma_interrupt(void *p, uint32_t flags) {
(void)p;
OSAL_IRQ_PROLOGUE();
dma_flags = flags;
dmaStreamClearInterrupt(dmastp);
if (flags & (STM32_DMA_ISR_FEIF | STM32_DMA_ISR_TEIF } STM32_DMA_ISR_DMEIF)) {
/*
* DMA transfer error, FIFO error or Direct mode error.
* See 9.34.19 of RM0430.
*/
dmaStreamClearInterrupt(dmastp);
TIM1->DIER &= ~TIM_DIER_TDE;
dma_error = true;
return;
}
TIM1->SR &= ~TIM_SR_TIF;
OSAL_IRQ_EPILOGUE();
if ((flags & STM32_DMA_ISR_TCIF) != 0) {
/*
* If DMA has run to end within a frame then this is an error.
* In single buffer mode DMA should always be terminated by VSYNC.
*
* Stop PCLK from LPTIM1 and disable TIM1 DMA trigger.
* Dont stop the DMA here. Its going to be stopped by the leading edge of VSYNC.
*/
TIM1->DIER &= ~TIM_DIER_TDE;
LPTIM1->CR &= ~LPTIM_CR_CNTSTRT;
dma_error = true;
return;
}
}
#endif /* USE_OV5640_DMA_DBM */
/*
* The LPTIM interrupt handler.
* TODO: Remove LPTIM1 interrupt.
* Not needed in COUNTMODE.
* LPTIM clock kernel initialises in 2 internal clock cycles
*/
OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) {
/* Note:
* STM32F4 vectors defined by Chibios currently stop at 98.
* Need to allocate more space in vector table for LPTIM1.
* LPTIM1 is vector 97. Vector table is expanded in increments of 8.
* Change CORTEX_NUM_PARAMS in cmparams.h to 106.
* LPTIM1 is vector 97.
* Check CORTEX_NUM_PARAMS in cmparams.h >= 106.
* Vector table is expanded in increments of 8.
*/
OSAL_IRQ_PROLOGUE();
/* Reset interrupt flag for ARR. */
@ -845,17 +1053,20 @@ OSAL_IRQ_HANDLER(STM32_LPTIM1_HANDLER) {
}
/*
* Note: VSYNC is a pulse the full length of a frame.
* This is contrary to the OV5640 datasheet which shows VSYNC as pulses.
*/
* VSYNC is asserted during a frame.
* See OV5640 datasheet for details.
*/
CH_IRQ_HANDLER(Vector5C) {
CH_IRQ_PROLOGUE();
if (LptimRdy) {
// VSYNC handling
if(!vsync) {
// Increase AHB clock to 48 MHz
set48MHz();
/*
* Rising edge of VSYNC after LPTIM1 has been initiualized.
* Rising edge of VSYNC after LPTIM1 has been initialised.
* Start DMA channel.
* Enable TIM1 trigger of DMA.
*/
@ -863,28 +1074,31 @@ CH_IRQ_HANDLER(Vector5C) {
TIM1->DIER |= TIM_DIER_TDE;
vsync = true;
} else {
/* VSYNC falling edge - end of JPEG frame.
// Reduce AHB clock to 6 MHz
set6MHz();
/* VSYNC leading with vsync true.
* This means end of capture for the frame.
* Stop & release the DMA channel.
* Disable TIM1 trigger of DMA and stop PCLK via LPTIM1
* These should have already been disabled in DMA interrupt if was filled.
* Disable TIM1 trigger of DMA and stop PCLK counting on LPTIM1.
* If buffer was filled in DMA then that is an error.
* We check that here.
*/
dma_stop();
TIM1->DIER &= ~TIM_DIER_TDE;
LPTIM1->CR &= ~LPTIM_CR_CNTSTRT;
/* Disable VYSNC edge interrupts. */
/*
* Disable VSYNC edge interrupts.
* Flag image capture complete.
*/
nvicDisableVector(EXTI1_IRQn);
image_finished = true;
vsync = false;
capture_finished = true;
}
} else {
/*
* LPTIM1 is not yet initialised.
* So we enable LPTIM1 to start counting.
* The PCLK should be low at the leading edge of VSYNC.
* Thus we get a clean start of LPTIM1 clocking on next leading edge of PCLK.
* After the LPTIM core is initialised PCLK and LPTIM_OUT >should< be synchronous.
* This needs to be verified as the ST RM document is not precise on this matter.
*/
LPTIM1->CR |= LPTIM_CR_CNTSTRT;
}
@ -895,6 +1109,7 @@ CH_IRQ_HANDLER(Vector5C) {
bool OV5640_Capture(void)
{
/*
* Note:
* If there are no Chibios devices enabled that use DMA then...
@ -903,8 +1118,8 @@ bool OV5640_Capture(void)
*/
/* Setup DMA for transfer on TIM1_TRIG - DMA2 stream 0, channel 6 */
dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 0));
uint32_t dmamode = STM32_DMA_CR_CHSEL(6) |
dmastp = STM32_DMA_STREAM(STM32_DMA_STREAM_ID(2, 0));
uint32_t dmamode = STM32_DMA_CR_CHSEL(6) |
STM32_DMA_CR_PL(3) |
STM32_DMA_CR_DIR_P2M |
STM32_DMA_CR_MSIZE_WORD |
@ -913,31 +1128,97 @@ bool OV5640_Capture(void)
STM32_DMA_CR_MINC |
STM32_DMA_CR_DMEIE |
STM32_DMA_CR_TEIE |
#if OV5640_USE_DMA_DBM == TRUE
STM32_DMA_CR_DBM |
STM32_DMA_CR_HTIE |
#endif
STM32_DMA_CR_TCIE;
dmaStreamAllocate(dmastp, 2, (stm32_dmaisr_t)dma_interrupt, NULL);
dmaStreamSetPeripheral(dmastp, &GPIOA->IDR); // We want to read the data from here
dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer); // Thats the buffer address
dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size); // Thats the buffer size
#if OV5640_USE_DMA_DBM == TRUE
/*
* Buffer address must be word aligned.
* Also note requirement for burst transfers from FIFO.
* Bursts from FIFO to memory must not cross a 1K address boundary.
* See RM0430 9.3.12
*
* TODO: To use DMA_FIFO_BURST_ALIGN in setting of ssdv buffer alignment.
* Currently this is set to 32 manually in config.c.
*/
dmaStreamSetMode(dmastp, dmamode); // Setup DMA
dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL);
dmaStreamClearInterrupt(dmastp);
if (((uint32_t)ov5640_conf->ram_buffer % DMA_FIFO_BURST_ALIGN) != 0) {
TRACE_ERROR("CAM > Buffer not allocated on DMA burst boundary");
return false;
}
/*
* Set the initial buffer addresses.
* The updating of DMA:MxAR is done in the the DMA interrupt function.
*/
dmaStreamSetMemory0(dmastp, &ov5640_conf->ram_buffer[0]);
dmaStreamSetMemory1(dmastp, &ov5640_conf->ram_buffer[DMA_SEGMENT_SIZE]);
dmaStreamSetTransactionSize(dmastp, DMA_SEGMENT_SIZE);
// Setup timer for PCLCK
/*
* Calculate the number of whole buffers.
* TODO: Make this include remainder memory as partial buffer?
*/
dma_buffers = (ov5640_conf->ram_size / DMA_SEGMENT_SIZE);
if (dma_buffers == 0) {
TRACE_ERROR("CAM > Capture buffer less than minimum DMA segment size");
return false;
}
/* Start with buffer index 0. */
dma_index = 0;
#else
dmaStreamSetMemory0(dmastp, ov5640_conf->ram_buffer);
dmaStreamSetTransactionSize(dmastp, ov5640_conf->ram_size);
#endif
dmaStreamSetMode(dmastp, dmamode); // Setup DMA
dmaStreamSetFIFO(dmastp, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL \
| STM32_DMA_FCR_FEIE);
dmaStreamClearInterrupt(dmastp);
dma_error = false;
dma_flags = 0;
// Setup timer for PCLK
rccResetLPTIM1();
rccEnableLPTIM1(FALSE);
/*
* The setting of CKSEL & COUNTMODE are not completely clear.
* The change below switches to using internal clock sampling the external clock.
*/
LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1);
/*
* LPTIM1 is run in external count mode (CKSEL = 0, COUNTMODE = 1).
* CKPOL is set so leading and trailing edge of PCLK increment the counter.
* The internal clocking (checking edges of LPTIM1_IN) is set to use APB.
* The internal clock must be >4 times the frequency of the input (PCLK).
* NOTE: This does not guarantee that LPTIM1_OUT is coincident with PCLK.
* Depending on PCLK state when LPTIM1 is enabled OUT may get inverted.
*
* LPTIM1 is enabled on the VSYNC edge interrupt.
* After enabling LPTIM1 wait for the first interrupt (ARRIF).
* The interrupt must be disabled on the first interrupt (else flood).
*
* LPTIM1_OUT is gated to TIM1 internal trigger input 2.
*/
LPTIM1->CFGR = (LPTIM_CFGR_COUNTMODE | LPTIM_CFGR_CKPOL_1 | LPTIM_CFGR_WAVPOL);
LPTIM1->OR |= LPTIM_OR_TIM1_ITR2_RMP;
LPTIM1->CR |= LPTIM_CR_ENABLE;
LPTIM1->IER |= LPTIM_IER_ARRMIE; // We need this interrupt to fire only once when the LPTIM kernel is ready
LPTIM1->IER |= LPTIM_IER_ARRMIE;
/*
* When LPTIM1 is enabled and ready LPTIM1_OUT will be not set.
* WAVPOL inverts LPTIM1_OUT so it is not set.
* On the next PCLK edge LPTIM1 will count and match ARR.
* LPTIM1_OUT will set briefly and then clear again due ARR match.
* This triggers TIM1 with the short pulse from LPTIM1_OUT.
* TODO:
* This use of LPTIM1 works probably by good luck for now.
* Switch to direct triggering of TIM using Capture input is better.
* Requires a PCB change.
*/
LPTIM1->CMP = 0;
LPTIM1->ARR = 1;
@ -947,53 +1228,51 @@ bool OV5640_Capture(void)
/*
* Setup slave timer to trigger DMA.
* We have to use TIM1 because...
* > it can be triggered from LPTIM1
* > and TIM1_TRIG is in DMA2 and we need DMA2 for peripheral -> memory transfer
* We use TIM1 because it can be triggered from LPTIM1.
*/
rccResetTIM1();
rccEnableTIM1(FALSE);
TIM1->SMCR = TIM_SMCR_TS_1; // Select ITR2 as trigger
TIM1->SMCR |= TIM_SMCR_SMS_2; // Set timer in reset mode
/*
* IC1 is mapped to TRC which means we are in Input mode.
* The timer will reset and trigger DMA on the leading edge of LPTIM1_OUT.
* The counter will count up while LPTIM1_OUT is high.
* We don't care what the count is.
* We just use the DMA initiated by the trigger which is independant of counting.
*/
/*
* TIM1_IC1 is mapped to TRC which means we are in trigger input mode.
* TIM1 is set in slave reset mode with input from ITR2.
* The timer will reset and trigger DMA on the leading edge of LPTIM1_OUT.
* The counter will count up while LPTIM1_OUT is high.
* We don't care about the count.
* We simply use the DMA initiated by the trigger input.
*/
TIM1->SMCR = TIM_SMCR_TS_1;
TIM1->SMCR |= TIM_SMCR_SMS_2;
TIM1->CCMR1 |= (TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC1S_1);
image_finished = false;
capture_finished = false;
vsync = false;
// Setup EXTI: EXTI1 PC for PC1 (VSYNC) and EXIT2 PC for PC2 (HREF)
// Setup EXTI: EXTI1 PC for PC1 (VSYNC)
SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PC;
EXTI->IMR = EXTI_IMR_MR1; // Activate interrupt for chan1 (=>PC1) and chan2 (=>PC2)
EXTI->RTSR = EXTI_RTSR_TR1; // Listen on rising edge
nvicEnableVector(EXTI1_IRQn, 1); // Enable interrupt
do { // Have a look for some bytes in memory for testing if capturing works
TRACE_INFO("CAM > Capturing");
chThdSleepMilliseconds(100);
} while(!image_finished);
TRACE_INFO("CAM > ... capturing");
chThdSleepMilliseconds(200);
} while(!capture_finished && !dma_error);
uint32_t soi; // Start of Image
for(soi=0; soi<65533; soi++)
if(ov5640_conf->ram_buffer[soi] == 0xFF && ov5640_conf->ram_buffer[soi+1] == 0xE0)
break;
if(soi == 65533) {
TRACE_ERROR("CAM > Could not find SOI flag");
return false; // We failed to sample the picture correctly because we didn't find the JPEG SOI flag
if (dma_error) {
if (dma_flags & STM32_DMA_ISR_HTIF)
TRACE_ERROR("CAM > DMA abort - last buffer segment")
if (dma_flags & STM32_DMA_ISR_FEIF)
TRACE_ERROR("CAM > DMA FIFO error")
if (dma_flags & STM32_DMA_ISR_TEIF)
TRACE_ERROR("CAM > DMA stream transfer error")
if (dma_flags & STM32_DMA_ISR_DMEIF)
TRACE_ERROR("CAM > DMA direct mode error")
TRACE_ERROR("CAM > Error capturing image");
return false;
}
// Found SOI, move bytes
for(uint32_t i=0; i<65535; i++)
ov5640_conf->ram_buffer[i] = ov5640_conf->ram_buffer[i+soi];
TRACE_INFO("CAM > Capture finished");
TRACE_INFO("CAM > Capture success");
return true;
}
@ -1026,21 +1305,28 @@ void OV5640_InitGPIO(void)
void OV5640_TransmitConfig(void)
{
chThdSleepMilliseconds(1000);
TRACE_INFO("CAM > ... Software reset");
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3103, 0x11);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3008, 0x82);
chThdSleepMilliseconds(100);
TRACE_INFO("CAM > ... Initialization");
for(uint32_t i=0; (OV5640YUV_Sensor_Dvp_Init[i].reg != 0xffff) || (OV5640YUV_Sensor_Dvp_Init[i].val != 0xff); i++)
I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640YUV_Sensor_Dvp_Init[i].reg, OV5640YUV_Sensor_Dvp_Init[i].val);
chThdSleepMilliseconds(500);
TRACE_INFO("CAM > ... Configure JPEG");
for(uint32_t i=0; (OV5640_JPEG_QSXGA[i].reg != 0xffff) || (OV5640_JPEG_QSXGA[i].val != 0xff); i++)
I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_JPEG_QSXGA[i].reg, OV5640_JPEG_QSXGA[i].val);
TRACE_INFO("CAM > ... Configure Resolution");
switch(ov5640_conf->res) {
case RES_QQVGA:
for(uint32_t i=0; (OV5640_QSXGA2QQVGA[i].reg != 0xffff) || (OV5640_QSXGA2QQVGA[i].val != 0xff); i++)
I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_QSXGA2QQVGA[i].reg, OV5640_QSXGA2QQVGA[i].val);
break;
case RES_QVGA:
for(uint32_t i=0; (OV5640_QSXGA2QVGA[i].reg != 0xffff) || (OV5640_QSXGA2QVGA[i].val != 0xff); i++)
I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val);
@ -1066,8 +1352,7 @@ void OV5640_TransmitConfig(void)
I2C_write8_16bitreg(OV5640_I2C_ADR, OV5640_QSXGA2QVGA[i].reg, OV5640_QSXGA2QVGA[i].val);
}
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x4407, 0x04); // Quantization scale
TRACE_INFO("CAM > ... Light Mode: Auto");
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3406, 0x00);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3400, 0x04);
@ -1078,9 +1363,9 @@ void OV5640_TransmitConfig(void)
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3405, 0x00);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // lanuch group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 );
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5183 ,0x0 );
TRACE_INFO("CAM > ... Saturation: 0");
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5381, 0x1c);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5382, 0x5a);
@ -1096,33 +1381,20 @@ void OV5640_TransmitConfig(void)
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3
TRACE_INFO("CAM > ... Brightness: 0");
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5587, 0x00);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5588, 0x01);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3
TRACE_INFO("CAM > ... Contrast: 0");
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5586, 0x20);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5585, 0x00);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x03); // start group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5580, 0x06);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5583, 0x40); // sat U
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5584, 0x10); // sat V
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x5003, 0x08);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0x13); // end group 3
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a0f, 0x38);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a10, 0x30);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a11, 0x61);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1b, 0x38);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1e, 0x30);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3a1f, 0x10);
I2C_write8_16bitreg(OV5640_I2C_ADR, 0x3212, 0xa3); // launch group 3
}
void OV5640_init(ssdv_conf_t *config) {
@ -1139,13 +1411,15 @@ void OV5640_init(ssdv_conf_t *config) {
// Power on OV5640
TRACE_INFO("CAM > Switch on");
palSetLine(LINE_CAM_EN); // Switch on camera
palSetLine(LINE_CAM_RESET); // Toggle reset
palSetLine(LINE_CAM_RESET); // Toggle reset
chThdSleepMilliseconds(1000);
// Send settings to OV5640
TRACE_INFO("CAM > Transmit config to camera");
OV5640_TransmitConfig();
chThdSleepMilliseconds(3000);
chThdSleepMilliseconds(1000);
}
void OV5640_deinit(void) {
@ -1196,3 +1470,12 @@ bool OV5640_isAvailable(void)
return ret;
}
uint32_t OV5640_getLightIntensity(void)
{
uint8_t val1,val2,val3;
I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1B, &val1);
I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1C, &val2);
I2C_read8_16bitreg(OV5640_I2C_ADR, 0x3C1D, &val3);
return (val1 << 16) | (val2 << 8) | val3;
}

Wyświetl plik

@ -9,6 +9,8 @@
#include "hal.h"
#include "types.h"
#define OV5640_USE_DMA_DBM TRUE
bool OV5640_Snapshot2RAM(void);
bool OV5640_Capture(void);
void OV5640_InitGPIO(void);
@ -18,5 +20,6 @@ void OV5640_TransmitConfig(void);
void OV5640_init(ssdv_conf_t *config);
void OV5640_deinit(void);
bool OV5640_isAvailable(void);
uint32_t OV5640_getLightIntensity(void);
#endif

Wyświetl plik

@ -4,6 +4,7 @@
#include "pi2c.h"
#include "pac1720.h"
#include "padc.h"
#include <stdlib.h>
/*
* FSP = FSC * FSV
@ -19,6 +20,8 @@
static int32_t pac1720_pbat;
static int32_t pac1720_pbat_counter;
static int32_t pac1720_rbat;
static int32_t pac1720_rbat_counter;
int16_t pac1720_getPbat(void) {
int32_t fsp = FSV * FSC;
@ -33,34 +36,10 @@ int16_t pac1720_getPbat(void) {
}
}
int16_t pac1720_getIsol(void) {
if(isUsbConnected()) // USB not connected
{
// Short solar cells
palClearLine(LINE_SOL_SHORT_EN);
chThdSleepMilliseconds(300); // Wait a little bit to measure a correct value
int16_t val;
uint8_t ret = I2C_read16(PAC1720_ADDRESS, PAC1720_CH1_VSENSE_HIGH, (uint16_t*)&val);
// Unshort solar cells
palSetLine(LINE_SOL_SHORT_EN);
// Calculate solar current
if(ret) {
val = val < 0 ? 0 : val >> 4; // Current can only flow in one direction (negative values are inaccurate readings)
return val * FSC / DENO;
} else {
return 0; // PAC1720 not available (maybe Vcc too low)
}
}
return 0; // USB connected (we dont want to short USB)
}
int16_t pac1720_getAvgPbat(void) {
// Return current value if time interval too short
if(!pac1720_pbat_counter)
pac1720_getPbat();
return pac1720_getPbat();
// Calculate average power
int16_t ret = pac1720_pbat / pac1720_pbat_counter;
@ -72,7 +51,7 @@ int16_t pac1720_getAvgPbat(void) {
return ret;
}
uint16_t pac1720_getBatteryVoltage(void) {
uint16_t pac1720_getVbat(void) {
uint16_t val;
if(!I2C_read16(PAC1720_ADDRESS, PAC1720_CH2_VSOURCE_HIGH, &val))
return 0; // PAC1720 not available (maybe Vcc too low)
@ -80,7 +59,22 @@ uint16_t pac1720_getBatteryVoltage(void) {
return (val >> 5) * 20000 / 0x400;
}
uint16_t pac1720_getSolarVoltage(void) {
int16_t pac1720_getAvgRbat(void) {
// Return current value if time interval too short
if(!pac1720_rbat_counter)
return 0;
// Calculate average power
int16_t ret = pac1720_rbat / pac1720_rbat_counter;
// Reset current measurement
pac1720_rbat = 0;
pac1720_rbat_counter = 0;
return ret;
}
uint16_t pac1720_getVsol(void) {
uint16_t val;
if(!I2C_read16(PAC1720_ADDRESS, PAC1720_CH1_VSOURCE_HIGH, &val))
return 0; // PAC1720 not available (maybe Vcc too low)
@ -101,12 +95,42 @@ THD_FUNCTION(pac1720_thd, arg)
{
(void)arg;
uint32_t counter = 0;
int32_t u1 = 999999;
int32_t p1 = 999999;
int32_t u2 = -999999;
int32_t p2 = -999999;
while(true)
{
// Sample data
int32_t v = pac1720_getVbat();
int32_t p = pac1720_getPbat();
// Measure battery power
pac1720_pbat += pac1720_getPbat();
pac1720_pbat += p;
pac1720_pbat_counter++;
// Measure battery impedance
if(p < p1) {
u1 = v;
p1 = p;
}
if(p > p2) {
u2 = v;
p2 = p;
}
if(++counter%10 == 0 && abs(p1-p2) > 100 && abs(u1-u2) > 0 && p1*u2 != p2*u1)
{
int32_t rbat = abs((u1*u2*(u1-u2)) / (p1*u2 - p2*u1));
pac1720_rbat += rbat;
pac1720_rbat_counter++;
u1 = 999999;
p1 = 999999;
u2 = -999999;
p2 = -999999;
}
chThdSleepMilliseconds(50);
}
}
@ -128,6 +152,7 @@ void pac1720_init(void)
I2C_write8(PAC1720_ADDRESS, PAC1720_CH2_VSENSE_SAMP_CONFIG, 0x5F);
I2C_write8(PAC1720_ADDRESS, PAC1720_V_SOURCE_SAMP_CONFIG, 0xFF);
TRACE_INFO("PAC > Init PAC1720 continuous measurement");
chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(256), "PAC1720", NORMALPRIO, pac1720_thd, NULL);
//TRACE_INFO("PAC > Init PAC1720 continuous measurement");
chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(512), "PAC1720", NORMALPRIO, pac1720_thd, NULL);
}

Wyświetl plik

@ -40,9 +40,9 @@
#define PAC1720_CH2_VSENSE_LIMIT_LOW 0x1C
#define PAC1720_CH1_VSOURCE_LIMIT_HIGH 0x1D
#define PAC1720_CH2_VSOURCE_LIMIT_HIGH 0x1E
#define PAC1720_CH1_VSOURCE_LIMIT_LOW 0x1F
#define PAC1720_CH2_VSOURCE_LIMIT_LOW 0x20
#define PAC1720_CH2_VSOURCE_LIMIT_HIGH 0x1E
#define PAC1720_CH1_VSOURCE_LIMIT_LOW 0x1F
#define PAC1720_CH2_VSOURCE_LIMIT_LOW 0x20
#define PAC1720_PRODUCT_ID 0xFD
#define PAC1720_MANUFACTURER_ID 0xFE
@ -53,8 +53,9 @@ void pac1720_init(void);
int16_t pac1720_getIsol(void);
int16_t pac1720_getPbat(void);
int16_t pac1720_getAvgPbat(void);
uint16_t pac1720_getBatteryVoltage(void);
uint16_t pac1720_getSolarVoltage(void);
uint16_t pac1720_getVbat(void);
int16_t pac1720_getAvgRbat(void);
uint16_t pac1720_getVsol(void);
bool pac1720_isAvailable(void);
#endif

Wyświetl plik

@ -7,7 +7,6 @@
#include "ch.h"
#include "hal.h"
#include "si4464.h"
#include "modules.h"
#include "debug.h"
#include "types.h"
#include <string.h>
@ -15,7 +14,7 @@
static const SPIConfig ls_spicfg = {
.ssport = PAL_PORT(LINE_RADIO_CS),
.sspad = PAL_PAD(LINE_RADIO_CS),
.cr1 = SPI_CR1_MSTR | SPI_CR1_BR_0
.cr1 = SPI_CR1_MSTR
};
#define getSPIDriver() &ls_spicfg
@ -29,20 +28,17 @@ bool initialized = false;
*/
void Si4464_Init(void) {
// Reset radio)
radioShutdown();
Si4464_shutdown();
chThdSleepMilliseconds(10);
// Initialize SPI
palSetLineMode(LINE_SPI_SCK, PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST); // SCK
palSetLineMode(LINE_SPI_MISO, PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST); // MISO
palSetLineMode(LINE_SPI_MOSI, PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST); // MOSI
// Configure SPI pins
palSetLineMode(LINE_SPI_SCK, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // SCK
palSetLineMode(LINE_SPI_MISO, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // MISO
palSetLineMode(LINE_SPI_MOSI, PAL_MODE_ALTERNATE(6) | PAL_STM32_OSPEED_HIGHEST); // MOSI
palSetLineMode(LINE_RADIO_CS, PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); // RADIO CS
palSetLineMode(LINE_RADIO_SDN, PAL_MODE_OUTPUT_PUSHPULL); // RADIO SDN
palSetLine(LINE_RADIO_CS);
// Configure pins
palSetLineMode(LINE_RADIO_SDN, PAL_MODE_OUTPUT_PUSHPULL); // RADIO SDN
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // RADIO GPIO1
// Power up transmitter
palClearLine(LINE_RADIO_SDN); // Radio SDN low (power up transmitter)
chThdSleepMilliseconds(10); // Wait for transmitter to power up
@ -56,20 +52,6 @@ void Si4464_Init(void) {
Si4464_write(init_command, 7);
chThdSleepMilliseconds(25);
// Set transmitter GPIOs
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Temperature readout
TRACE_INFO("SI > Transmitter temperature %d degC", Si4464_getTemperature());
initialized = true;
@ -80,29 +62,27 @@ void Si4464_write(uint8_t* txData, uint32_t len) {
uint8_t rxData[len];
// SPI transfer
spiAcquireBus(&SPID1);
spiStart(&SPID1, getSPIDriver());
spiSelect(&SPID1);
spiExchange(&SPID1, len, txData, rxData);
spiUnselect(&SPID1);
spiReleaseBus(&SPID1);
spiAcquireBus(&SPID3);
spiStart(&SPID3, getSPIDriver());
spiSelect(&SPID3);
spiExchange(&SPID3, len, txData, rxData);
spiUnselect(&SPID3);
// Reqest ACK by Si4464
uint32_t counter = 0; // FIXME: Sometimes CTS is not returned by Si4464 correctly
rxData[1] = 0x00;
while(rxData[1] != 0xFF && ++counter < 2000) {
while(rxData[1] != 0xFF) {
// Request ACK by Si4464
uint8_t rx_ready[] = {0x44};
// SPI transfer
spiAcquireBus(&SPID1);
spiStart(&SPID1, getSPIDriver());
spiSelect(&SPID1);
spiExchange(&SPID1, 3, rx_ready, rxData);
spiUnselect(&SPID1);
spiReleaseBus(&SPID1);
spiSelect(&SPID3);
spiExchange(&SPID3, 3, rx_ready, rxData);
spiUnselect(&SPID3);
}
spiStop(&SPID3);
spiReleaseBus(&SPID3);
}
/**
@ -112,30 +92,28 @@ void Si4464_read(uint8_t* txData, uint32_t txlen, uint8_t* rxData, uint32_t rxle
// Transmit data by SPI
uint8_t null_spi[txlen];
// SPI transfer
spiAcquireBus(&SPID1);
spiStart(&SPID1, getSPIDriver());
spiSelect(&SPID1);
spiExchange(&SPID1, txlen, txData, null_spi);
spiUnselect(&SPID1);
spiReleaseBus(&SPID1);
spiAcquireBus(&SPID3);
spiStart(&SPID3, getSPIDriver());
spiSelect(&SPID3);
spiExchange(&SPID3, txlen, txData, null_spi);
spiUnselect(&SPID3);
// Reqest ACK by Si4464
uint32_t counter = 0; // FIXME: Sometimes CTS is not returned by Si4464 correctly
rxData[1] = 0x00;
while(rxData[1] != 0xFF && ++counter < 2000) {
while(rxData[1] != 0xFF) {
// Request ACK by Si4464
uint16_t rx_ready[rxlen];
rx_ready[0] = 0x44;
// SPI transfer
spiAcquireBus(&SPID1);
spiStart(&SPID1, getSPIDriver());
spiSelect(&SPID1);
spiExchange(&SPID1, rxlen, rx_ready, rxData);
spiUnselect(&SPID1);
spiReleaseBus(&SPID1);
spiSelect(&SPID3);
spiExchange(&SPID3, rxlen, rx_ready, rxData);
spiUnselect(&SPID3);
}
spiStop(&SPID3);
spiReleaseBus(&SPID3);
}
void setFrequency(uint32_t freq, uint16_t shift) {
@ -195,6 +173,21 @@ void setShift(uint16_t shift) {
}
void setModemAFSK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Disable preamble
uint8_t disable_preamble[] = {0x11, 0x10, 0x01, 0x00, 0x00};
Si4464_write(disable_preamble, 5);
@ -230,14 +223,44 @@ void setModemAFSK(void) {
}
void setModemOOK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Use OOK from async GPIO1
uint8_t use_ook[] = {0x11, 0x20, 0x01, 0x00, 0xA9};
Si4464_write(use_ook, 5);
}
void setModem2FSK(void) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_OUTPUT_PUSHPULL); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x44, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Initialize high tone
RADIO_MOD_GPIO(HIGH);
RADIO_WRITE_GPIO(HIGH);
// use 2FSK from async GPIO1
uint8_t use_2fsk[] = {0x11, 0x20, 0x01, 0x00, 0xAA};
@ -245,6 +268,25 @@ void setModem2FSK(void) {
}
void setModem2GFSK(gfsk_conf_t* conf) {
// Set transmitter GPIOs
palSetLineMode(LINE_RADIO_GPIO, PAL_MODE_INPUT); // GPIO1
uint8_t gpio_pin_cfg_command[] = {
0x13, // Command type = GPIO settings
0x00, // GPIO0 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x23, // GPIO1 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO2 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // GPIO3 0 - PULL_CTL[1bit] - GPIO_MODE[6bit]
0x00, // NIRQ
0x00, // SDO
0x00 // GEN_CONFIG
};
Si4464_write(gpio_pin_cfg_command, 8);
chThdSleepMilliseconds(25);
// Set FIFO empty interrupt threshold (32 byte)
uint8_t set_fifo_irq[] = {0x11, 0x12, 0x01, 0x0B, 0x20};
Si4464_write(set_fifo_irq, 5);
// Disable preamble
uint8_t disable_preamble[] = {0x11, 0x10, 0x01, 0x00, 0x00};
Si4464_write(disable_preamble, 5);
@ -267,8 +309,12 @@ void setModem2GFSK(gfsk_conf_t* conf) {
Si4464_write(setup_data_rate, 7);
// Use 2GFSK from async GPIO1
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x2B};
uint8_t use_2gfsk[] = {0x11, 0x20, 0x01, 0x00, 0x23};
Si4464_write(use_2gfsk, 5);
// transmit LSB first
uint8_t use_lsb_first[] = {0x11, 0x12, 0x01, 0x06, 0x01};
Si4464_write(use_lsb_first, 5);
}
void setPowerLevel(int8_t level) {
@ -291,10 +337,10 @@ void stopTx(void) {
Si4464_write(change_state_command, 2);
}
void radioShutdown(void) {
void Si4464_shutdown(void) {
palSetLine(LINE_RADIO_SDN); // Power down chip
palSetLine(LINE_IO_LED1); // Set indication LED
RADIO_MOD_GPIO(false); // Set GPIO1 low
RADIO_WRITE_GPIO(false); // Set GPIO1 low
initialized = false;
}

Wyświetl plik

@ -6,8 +6,19 @@
#include "defines.h"
#include "types.h"
#define RADIO_MOD_GPIO(state) palWriteLine(LINE_RADIO_GPIO, state)
#define inRadioBand(freq) (RADIO_MIN_FREQ <= (freq) && (freq) <= RADIO_MAX_FREQ)
#define RADIO_WRITE_GPIO(state) palWriteLine(LINE_RADIO_GPIO, state)
#define RADIO_READ_GPIO() palReadLine(LINE_RADIO_GPIO)
#define inRadioBand(freq) (RADIO_MIN_FREQ <= (freq) && (freq) <= RADIO_MAX_FREQ)
#define SI4464_STATE_NOCHANGE 0
#define SI4464_STATE_SLEEP 1
#define SI4464_STATE_SPI_ACTIVE 2
#define SI4464_STATE_READY 3
#define SI4464_STATE_READY2 4
#define SI4464_STATE_TX_TUNE 5
#define SI4464_STATE_RX_TUNE 6
#define SI4464_STATE_TX 7
#define SI4464_STATE_RX 8
void Si4464_Init(void);
void Si4464_write(uint8_t* txData, uint32_t len);
@ -21,7 +32,7 @@ void setDeviation(uint32_t deviation);
void setPowerLevel(int8_t level);
void startTx(uint16_t size);
void stopTx(void);
void radioShutdown(void);
void Si4464_shutdown(void);
bool radioTune(uint32_t frequency, uint16_t shift, int8_t level, uint16_t size);
void Si4464_writeFIFO(uint8_t *msg, uint8_t size);
uint8_t Si4464_freeFIFO(void);

Wyświetl plik

@ -188,8 +188,6 @@ bool gps_get_fix(gpsFix_t *fix) {
return false;
}
TRACE_INFO("GPS > PVT Polling OK");
fix->num_svs = response[23];
fix->type = response[20];
@ -217,6 +215,12 @@ bool gps_get_fix(gpsFix_t *fix) {
fix->alt = (uint16_t)alt_tmp;
}
TRACE_INFO("GPS > PVT Polling OK time=%04d-%02d-%02d %02d:%02d:%02d lat=%d.%05d lon=%d.%05d alt=%dm sats=%d",
fix->time.year, fix->time.month, fix->time.day, fix->time.hour, fix->time.minute, fix->time.day,
fix->lat/10000000, (fix->lat > 0 ? 1:-1)*(fix->lat/100)%100000, fix->lon/10000000, (fix->lon > 0 ? 1:-1)*(fix->lon/100)%100000,
fix->alt, fix->num_svs
);
return true;
}

Wyświetl plik

@ -68,15 +68,20 @@ void doConversion(void)
deinitADC();
}
uint16_t getBatteryVoltageMV(void)
uint16_t getBatteryVoltageMV_STM32(void)
{
doConversion();
uint16_t vbat = samples[2] * vcc_ref * DIVIDER_VBAT / 4096;
return samples[2] * vcc_ref * DIVIDER_VBAT / 4096;
}
uint16_t getBatteryVoltageMV(void)
{
uint16_t vbat = getBatteryVoltageMV_STM32();
// Get voltage from PAC1720 (PAC1720 returns false redings below 2.35V)
if(vbat >= 2500)
{
uint16_t vbat_pac = pac1720_getBatteryVoltage(); // Get value from PAC1720
uint16_t vbat_pac = pac1720_getVbat(); // Get value from PAC1720
if(vbat_pac) // Apply it if valid
vbat = vbat_pac;
}
@ -86,10 +91,12 @@ uint16_t getBatteryVoltageMV(void)
uint16_t getSolarVoltageMV(void)
{
uint16_t vbat = getBatteryVoltageMV_STM32();
// Get voltage from PAC1720 (PAC1720 returns false redings below 2.35V)
if(getBatteryVoltageMV() >= 2500)
if(vbat >= 2500)
{
uint16_t vsol_pac = pac1720_getSolarVoltage(); // Get value from PAC1720
uint16_t vsol_pac = pac1720_getVsol(); // Get value from PAC1720
if(vsol_pac)
return vsol_pac;
}

Wyświetl plik

@ -8,6 +8,7 @@
void initADC(void);
void deinitADC(void);
uint16_t getBatteryVoltageMV_STM32(void);
uint16_t getBatteryVoltageMV(void);
uint16_t getSolarVoltageMV(void);
uint16_t getUSBVoltageMV(void);

Wyświetl plik

@ -10,50 +10,49 @@
const I2CConfig _i2cfg = {
OPMODE_I2C,
50000,
STD_DUTY_CYCLE,
200000,
FAST_DUTY_CYCLE_2,
};
static bool I2C_transmit(I2CDriver *driver, uint8_t addr, uint8_t *txbuf, uint32_t txbytes, uint8_t *rxbuf, uint32_t rxbytes, systime_t timeout) {
i2cAcquireBus(driver);
msg_t i2c_status = i2cMasterTransmitTimeout(driver, addr, txbuf, txbytes, rxbuf, rxbytes, timeout);
static bool I2C_transmit(uint8_t addr, uint8_t *txbuf, uint32_t txbytes, uint8_t *rxbuf, uint32_t rxbytes, systime_t timeout) {
i2cAcquireBus(I2C_DRIVER);
i2cStart(I2C_DRIVER, &_i2cfg);
msg_t i2c_status = i2cMasterTransmitTimeout(I2C_DRIVER, addr, txbuf, txbytes, rxbuf, rxbytes, timeout);
i2cStop(I2C_DRIVER);
i2cReleaseBus(I2C_DRIVER);
if(i2c_status == MSG_TIMEOUT) { // Restart I2C at timeout
TRACE_ERROR("I2C > TIMEOUT > RESTART (ADDR 0x%02x)", addr);
i2cStop(driver);
i2cStart(driver, &_i2cfg);
TRACE_ERROR("I2C > TIMEOUT (ADDR 0x%02x)", addr);
} else if(i2c_status == MSG_RESET) {
TRACE_ERROR("I2C > RESET (ADDR 0x%02x)", addr);
}
i2cReleaseBus(driver);
return i2c_status == MSG_OK;
}
void pi2cInit(void)
{
TRACE_INFO("I2C > Initialize I2C");
TRACE_INFO("I2C > Initialize I2C Pins");
palSetLineMode(LINE_I2C_SDA, PAL_MODE_ALTERNATE(4) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_OTYPE_OPENDRAIN); // SDA
palSetLineMode(LINE_I2C_SCL, PAL_MODE_ALTERNATE(4) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_OTYPE_OPENDRAIN); // SCL
i2cStart(&I2CD1, &_i2cfg);
}
bool I2C_write8(uint8_t address, uint8_t reg, uint8_t value)
{
uint8_t txbuf[] = {reg, value};
return I2C_transmit(I2C_DRIVER, address, txbuf, 2, NULL, 0, MS2ST(100));
return I2C_transmit(address, txbuf, 2, NULL, 0, MS2ST(100));
}
bool I2C_writeN(uint8_t address, uint8_t *txbuf, uint32_t length)
{
return I2C_transmit(I2C_DRIVER, address, txbuf, length, NULL, 0, MS2ST(100));
return I2C_transmit(address, txbuf, length, NULL, 0, MS2ST(100));
}
bool I2C_read8(uint8_t address, uint8_t reg, uint8_t *val)
{
uint8_t txbuf[] = {reg};
uint8_t rxbuf[1];
bool ret = I2C_transmit(I2C_DRIVER, address, txbuf, 1, rxbuf, 1, MS2ST(100));
bool ret = I2C_transmit(address, txbuf, 1, rxbuf, 1, MS2ST(100));
*val = rxbuf[0];
return ret;
}
@ -62,7 +61,7 @@ bool I2C_read16(uint8_t address, uint8_t reg, uint16_t *val)
{
uint8_t txbuf[] = {reg};
uint8_t rxbuf[2];
bool ret = I2C_transmit(I2C_DRIVER, address, txbuf, 1, rxbuf, 2, MS2ST(100));
bool ret = I2C_transmit(address, txbuf, 1, rxbuf, 2, MS2ST(100));
*val = (rxbuf[0] << 8) | rxbuf[1];
return ret;
}
@ -77,7 +76,7 @@ bool I2C_read8_16bitreg(uint8_t address, uint16_t reg, uint8_t *val) // 16bit re
{
uint8_t txbuf[] = {reg >> 8, reg & 0xFF};
uint8_t rxbuf[1];
bool ret = I2C_transmit(I2C_DRIVER, address, txbuf, 2, rxbuf, 1, MS2ST(100));
bool ret = I2C_transmit(address, txbuf, 2, rxbuf, 1, MS2ST(100));
*val = rxbuf[0];
return ret;
}
@ -85,6 +84,6 @@ bool I2C_read8_16bitreg(uint8_t address, uint16_t reg, uint8_t *val) // 16bit re
bool I2C_write8_16bitreg(uint8_t address, uint16_t reg, uint8_t value) // 16bit register (for OV5640)
{
uint8_t txbuf[] = {reg >> 8, reg & 0xFF, value};
return I2C_transmit(I2C_DRIVER, address, txbuf, 3, NULL, 0, MS2ST(100));
return I2C_transmit(address, txbuf, 3, NULL, 0, MS2ST(100));
}

Wyświetl plik

@ -2,7 +2,7 @@
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "threads.h"
#include "padc.h"
#include "usbcfg.h"
#include "shell.h"

Wyświetl plik

@ -1,268 +1,274 @@
/*
ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef MCUCONF_H
#define MCUCONF_H
/*
* STM32F4xx drivers configuration.
* The following settings override the default settings present in
* the various device driver implementation headers.
* Note that the settings for each driver only have effect if the whole
* driver is enabled in halconf.h.
*
* IRQ priorities:
* 15...0 Lowest...Highest.
*
* DMA priorities:
* 0...3 Lowest...Highest.
*/
#define STM32F4xx_MCUCONF
/*
* HAL driver system settings.
*/
#define STM32_NO_INIT FALSE
#define STM32_HSI_ENABLED FALSE
#define STM32_LSI_ENABLED TRUE
#define STM32_HSE_ENABLED TRUE
#define STM32_LSE_ENABLED FALSE
#define STM32_CLOCK48_REQUIRED TRUE
#define STM32_SW STM32_SW_PLL
#define STM32_PLLSRC STM32_PLLSRC_HSE
#define STM32_PLLM_VALUE 26
#define STM32_PLLN_VALUE 192
#define STM32_PLLP_VALUE 2
#define STM32_PLLQ_VALUE 4
#define STM32_HPRE STM32_HPRE_DIV1
#define STM32_PPRE1 STM32_PPRE1_DIV2
#define STM32_PPRE2 STM32_PPRE2_DIV1
#define STM32_RTCSEL STM32_RTCSEL_LSI
#define STM32_RTCPRE_VALUE 8
#define STM32_MCO1SEL STM32_MCO1SEL_HSE
#define STM32_MCO1PRE STM32_MCO1PRE_DIV2
#define STM32_MCO2SEL STM32_MCO2SEL_SYSCLK
#define STM32_MCO2PRE STM32_MCO2PRE_DIV5
#define STM32_I2SSRC STM32_I2SSRC_CKIN
#define STM32_PLLI2SN_VALUE 192
#define STM32_PLLI2SR_VALUE 2
#define STM32_PVD_ENABLE FALSE
#define STM32_PLS STM32_PLS_LEV0
#define STM32_BKPRAM_ENABLE FALSE
/*
* ADC driver system settings.
*/
#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV4
#define STM32_ADC_USE_ADC1 TRUE
#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
#define STM32_ADC_ADC1_DMA_PRIORITY 2
#define STM32_ADC_IRQ_PRIORITY 6
#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 6
/*
* EXT driver system settings.
*/
#define STM32_EXT_EXTI0_IRQ_PRIORITY 6
#define STM32_EXT_EXTI1_IRQ_PRIORITY 6
#define STM32_EXT_EXTI2_IRQ_PRIORITY 6
#define STM32_EXT_EXTI3_IRQ_PRIORITY 6
#define STM32_EXT_EXTI4_IRQ_PRIORITY 6
#define STM32_EXT_EXTI5_9_IRQ_PRIORITY 6
#define STM32_EXT_EXTI10_15_IRQ_PRIORITY 6
#define STM32_EXT_EXTI16_IRQ_PRIORITY 6
#define STM32_EXT_EXTI17_IRQ_PRIORITY 15
#define STM32_EXT_EXTI18_IRQ_PRIORITY 6
#define STM32_EXT_EXTI19_IRQ_PRIORITY 6
#define STM32_EXT_EXTI22_IRQ_PRIORITY 15
/*
* GPT driver system settings.
*/
#define STM32_GPT_USE_TIM1 FALSE
#define STM32_GPT_USE_TIM2 FALSE
#define STM32_GPT_USE_TIM3 FALSE
#define STM32_GPT_USE_TIM4 FALSE
#define STM32_GPT_USE_TIM5 FALSE
#define STM32_GPT_USE_TIM9 FALSE
#define STM32_GPT_USE_TIM11 FALSE
#define STM32_GPT_TIM1_IRQ_PRIORITY 7
#define STM32_GPT_TIM2_IRQ_PRIORITY 7
#define STM32_GPT_TIM3_IRQ_PRIORITY 7
#define STM32_GPT_TIM4_IRQ_PRIORITY 7
#define STM32_GPT_TIM5_IRQ_PRIORITY 7
#define STM32_GPT_TIM9_IRQ_PRIORITY 7
#define STM32_GPT_TIM11_IRQ_PRIORITY 7
/*
* I2C driver system settings.
*/
#define STM32_I2C_USE_I2C1 TRUE
#define STM32_I2C_USE_I2C2 FALSE
#define STM32_I2C_USE_I2C3 FALSE
#define STM32_I2C_BUSY_TIMEOUT 50
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_I2C_I2C1_IRQ_PRIORITY 5
#define STM32_I2C_I2C2_IRQ_PRIORITY 5
#define STM32_I2C_I2C3_IRQ_PRIORITY 5
#define STM32_I2C_I2C1_DMA_PRIORITY 3
#define STM32_I2C_I2C2_DMA_PRIORITY 3
#define STM32_I2C_I2C3_DMA_PRIORITY 3
#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
/*
* I2S driver system settings.
*/
#define STM32_I2S_USE_SPI2 FALSE
#define STM32_I2S_USE_SPI3 FALSE
#define STM32_I2S_SPI2_IRQ_PRIORITY 10
#define STM32_I2S_SPI3_IRQ_PRIORITY 10
#define STM32_I2S_SPI2_DMA_PRIORITY 1
#define STM32_I2S_SPI3_DMA_PRIORITY 1
#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_I2S_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
#define STM32_I2S_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure")
/*
* ICU driver system settings.
*/
#define STM32_ICU_USE_TIM1 FALSE
#define STM32_ICU_USE_TIM2 FALSE
#define STM32_ICU_USE_TIM3 FALSE
#define STM32_ICU_USE_TIM4 FALSE
#define STM32_ICU_USE_TIM5 FALSE
#define STM32_ICU_USE_TIM9 FALSE
#define STM32_ICU_TIM1_IRQ_PRIORITY 7
#define STM32_ICU_TIM2_IRQ_PRIORITY 7
#define STM32_ICU_TIM3_IRQ_PRIORITY 7
#define STM32_ICU_TIM4_IRQ_PRIORITY 7
#define STM32_ICU_TIM5_IRQ_PRIORITY 7
#define STM32_ICU_TIM9_IRQ_PRIORITY 7
/*
* PWM driver system settings.
*/
#define STM32_PWM_USE_ADVANCED FALSE
#define STM32_PWM_USE_TIM1 FALSE
#define STM32_PWM_USE_TIM2 FALSE
#define STM32_PWM_USE_TIM3 FALSE
#define STM32_PWM_USE_TIM4 FALSE
#define STM32_PWM_USE_TIM5 FALSE
#define STM32_PWM_USE_TIM9 FALSE
#define STM32_PWM_TIM1_IRQ_PRIORITY 7
#define STM32_PWM_TIM2_IRQ_PRIORITY 7
#define STM32_PWM_TIM3_IRQ_PRIORITY 7
#define STM32_PWM_TIM4_IRQ_PRIORITY 7
#define STM32_PWM_TIM5_IRQ_PRIORITY 7
#define STM32_PWM_TIM9_IRQ_PRIORITY 7
/*
* SERIAL driver system settings.
*/
#define STM32_SERIAL_USE_USART1 FALSE
#define STM32_SERIAL_USE_USART2 FALSE
#define STM32_SERIAL_USE_USART3 TRUE
#define STM32_SERIAL_USE_USART6 FALSE
#define STM32_SERIAL_USART1_PRIORITY 12
#define STM32_SERIAL_USART2_PRIORITY 12
#define STM32_SERIAL_USART3_PRIORITY 12
#define STM32_SERIAL_USART6_PRIORITY 12
/*
* SPI driver system settings.
*/
#define STM32_SPI_USE_SPI1 TRUE
#define STM32_SPI_USE_SPI2 FALSE
#define STM32_SPI_USE_SPI3 FALSE
#define STM32_SPI_USE_SPI4 FALSE
#define STM32_SPI_USE_SPI5 FALSE
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_SPI_SPI4_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
#define STM32_SPI_SPI4_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
#define STM32_SPI_SPI5_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
#define STM32_SPI_SPI5_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 6)
#define STM32_SPI_SPI1_DMA_PRIORITY 1
#define STM32_SPI_SPI2_DMA_PRIORITY 1
#define STM32_SPI_SPI3_DMA_PRIORITY 1
#define STM32_SPI_SPI4_DMA_PRIORITY 1
#define STM32_SPI_SPI5_DMA_PRIORITY 1
#define STM32_SPI_SPI1_IRQ_PRIORITY 10
#define STM32_SPI_SPI2_IRQ_PRIORITY 10
#define STM32_SPI_SPI3_IRQ_PRIORITY 10
#define STM32_SPI_SPI4_IRQ_PRIORITY 10
#define STM32_SPI_SPI5_IRQ_PRIORITY 10
#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
/*
* ST driver system settings.
*/
#define STM32_ST_IRQ_PRIORITY 8
#define STM32_ST_USE_TIMER 2
/*
* UART driver system settings.
*/
#define STM32_UART_USE_USART1 FALSE
#define STM32_UART_USE_USART2 FALSE
#define STM32_UART_USE_USART3 FALSE
#define STM32_UART_USE_USART6 FALSE
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_UART_USART6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)
#define STM32_UART_USART6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
#define STM32_UART_USART1_IRQ_PRIORITY 12
#define STM32_UART_USART2_IRQ_PRIORITY 12
#define STM32_UART_USART3_IRQ_PRIORITY 12
#define STM32_UART_USART6_IRQ_PRIORITY 12
#define STM32_UART_USART1_DMA_PRIORITY 0
#define STM32_UART_USART2_DMA_PRIORITY 0
#define STM32_UART_USART3_DMA_PRIORITY 0
#define STM32_UART_USART6_DMA_PRIORITY 0
#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
/*
* USB driver system settings.
*/
#define STM32_USB_USE_OTG1 TRUE
#define STM32_USB_OTG1_IRQ_PRIORITY 14
#define STM32_USB_OTG1_RX_FIFO_SIZE 512
#define STM32_USB_OTG_THREAD_PRIO LOWPRIO
#define STM32_USB_OTG_THREAD_STACK_SIZE 128
#define STM32_USB_OTGFIFO_FILL_BASEPRI 0
/*
* WDG driver system settings.
*/
#define STM32_WDG_USE_IWDG TRUE
#endif /* MCUCONF_H */
/*
ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef MCUCONF_H
#define MCUCONF_H
/*
* STM32F4xx drivers configuration.
* The following settings override the default settings present in
* the various device driver implementation headers.
* Note that the settings for each driver only have effect if the whole
* driver is enabled in halconf.h.
*
* IRQ priorities:
* 15...0 Lowest...Highest.
*
* DMA priorities:
* 0...3 Lowest...Highest.
*/
#define STM32F4xx_MCUCONF
/*
* HAL driver system settings.
*/
#define STM32_NO_INIT FALSE
#define STM32_HSI_ENABLED FALSE
#define STM32_LSI_ENABLED TRUE
#define STM32_HSE_ENABLED TRUE
#define STM32_LSE_ENABLED FALSE
#define STM32_CLOCK48_REQUIRED TRUE
#define STM32_SW STM32_SW_PLL
#define STM32_PLLSRC STM32_PLLSRC_HSE
#define STM32_PLLM_VALUE 26
#define STM32_PLLN_VALUE 192
#define STM32_PLLP_VALUE 4
#define STM32_PLLQ_VALUE 4
#define STM32_HPRE STM32_HPRE_DIV1
#define STM32_PPRE1 STM32_PPRE1_DIV1
#define STM32_PPRE2 STM32_PPRE2_DIV1
#define STM32_RTCSEL STM32_RTCSEL_LSI
#define STM32_RTCPRE_VALUE 8
#define STM32_MCO1SEL STM32_MCO1SEL_PLL
#define STM32_MCO1PRE STM32_MCO1PRE_DIV4
#define STM32_MCO2SEL STM32_MCO2SEL_SYSCLK
#define STM32_MCO2PRE STM32_MCO2PRE_DIV5
#define STM32_I2SSRC STM32_I2SSRC_CKIN
#define STM32_PLLI2SN_VALUE 192
#define STM32_PLLI2SR_VALUE 2
#define STM32_PVD_ENABLE FALSE
#define STM32_PLS STM32_PLS_LEV0
#define STM32_BKPRAM_ENABLE FALSE
/*
* PAL driver system settings.
*/
#define STM32_DISABLE_EXTI1_HANDLER
#define STM32_DISABLE_EXTI10_15_HANDLER
/*
* ADC driver system settings.
*/
#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV2
#define STM32_ADC_USE_ADC1 TRUE
#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
#define STM32_ADC_ADC1_DMA_PRIORITY 2
#define STM32_ADC_IRQ_PRIORITY 6
#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 6
/*
* EXT driver system settings.
*/
#define STM32_EXT_EXTI0_IRQ_PRIORITY 6
#define STM32_EXT_EXTI1_IRQ_PRIORITY 6
#define STM32_EXT_EXTI2_IRQ_PRIORITY 6
#define STM32_EXT_EXTI3_IRQ_PRIORITY 6
#define STM32_EXT_EXTI4_IRQ_PRIORITY 6
#define STM32_EXT_EXTI5_9_IRQ_PRIORITY 6
#define STM32_EXT_EXTI10_15_IRQ_PRIORITY 6
#define STM32_EXT_EXTI16_IRQ_PRIORITY 6
#define STM32_EXT_EXTI17_IRQ_PRIORITY 15
#define STM32_EXT_EXTI18_IRQ_PRIORITY 6
#define STM32_EXT_EXTI19_IRQ_PRIORITY 6
#define STM32_EXT_EXTI22_IRQ_PRIORITY 15
/*
* GPT driver system settings.
*/
#define STM32_GPT_USE_TIM1 FALSE
#define STM32_GPT_USE_TIM2 FALSE
#define STM32_GPT_USE_TIM3 FALSE
#define STM32_GPT_USE_TIM4 FALSE
#define STM32_GPT_USE_TIM5 FALSE
#define STM32_GPT_USE_TIM9 FALSE
#define STM32_GPT_USE_TIM11 FALSE
#define STM32_GPT_TIM1_IRQ_PRIORITY 7
#define STM32_GPT_TIM2_IRQ_PRIORITY 7
#define STM32_GPT_TIM3_IRQ_PRIORITY 7
#define STM32_GPT_TIM4_IRQ_PRIORITY 7
#define STM32_GPT_TIM5_IRQ_PRIORITY 7
#define STM32_GPT_TIM9_IRQ_PRIORITY 7
#define STM32_GPT_TIM11_IRQ_PRIORITY 7
/*
* I2C driver system settings.
*/
#define STM32_I2C_USE_I2C1 TRUE
#define STM32_I2C_USE_I2C2 FALSE
#define STM32_I2C_USE_I2C3 FALSE
#define STM32_I2C_BUSY_TIMEOUT 50
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_I2C_I2C1_IRQ_PRIORITY 5
#define STM32_I2C_I2C2_IRQ_PRIORITY 5
#define STM32_I2C_I2C3_IRQ_PRIORITY 5
#define STM32_I2C_I2C1_DMA_PRIORITY 1
#define STM32_I2C_I2C2_DMA_PRIORITY 1
#define STM32_I2C_I2C3_DMA_PRIORITY 1
#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
/*
* I2S driver system settings.
*/
#define STM32_I2S_USE_SPI2 FALSE
#define STM32_I2S_USE_SPI3 FALSE
#define STM32_I2S_SPI2_IRQ_PRIORITY 10
#define STM32_I2S_SPI3_IRQ_PRIORITY 10
#define STM32_I2S_SPI2_DMA_PRIORITY 1
#define STM32_I2S_SPI3_DMA_PRIORITY 1
#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_I2S_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
#define STM32_I2S_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure")
/*
* ICU driver system settings.
*/
#define STM32_ICU_USE_TIM1 FALSE
#define STM32_ICU_USE_TIM2 FALSE
#define STM32_ICU_USE_TIM3 FALSE
#define STM32_ICU_USE_TIM4 FALSE
#define STM32_ICU_USE_TIM5 FALSE
#define STM32_ICU_USE_TIM9 FALSE
#define STM32_ICU_TIM1_IRQ_PRIORITY 7
#define STM32_ICU_TIM2_IRQ_PRIORITY 7
#define STM32_ICU_TIM3_IRQ_PRIORITY 7
#define STM32_ICU_TIM4_IRQ_PRIORITY 7
#define STM32_ICU_TIM5_IRQ_PRIORITY 7
#define STM32_ICU_TIM9_IRQ_PRIORITY 7
/*
* PWM driver system settings.
*/
#define STM32_PWM_USE_ADVANCED FALSE
#define STM32_PWM_USE_TIM1 FALSE
#define STM32_PWM_USE_TIM2 FALSE
#define STM32_PWM_USE_TIM3 FALSE
#define STM32_PWM_USE_TIM4 FALSE
#define STM32_PWM_USE_TIM5 FALSE
#define STM32_PWM_USE_TIM9 FALSE
#define STM32_PWM_TIM1_IRQ_PRIORITY 7
#define STM32_PWM_TIM2_IRQ_PRIORITY 7
#define STM32_PWM_TIM3_IRQ_PRIORITY 7
#define STM32_PWM_TIM4_IRQ_PRIORITY 7
#define STM32_PWM_TIM5_IRQ_PRIORITY 7
#define STM32_PWM_TIM9_IRQ_PRIORITY 7
/*
* SERIAL driver system settings.
*/
#define STM32_SERIAL_USE_USART1 FALSE
#define STM32_SERIAL_USE_USART2 FALSE
#define STM32_SERIAL_USE_USART3 TRUE
#define STM32_SERIAL_USE_USART6 FALSE
#define STM32_SERIAL_USART1_PRIORITY 12
#define STM32_SERIAL_USART2_PRIORITY 12
#define STM32_SERIAL_USART3_PRIORITY 12
#define STM32_SERIAL_USART6_PRIORITY 12
/*
* SPI driver system settings.
*/
#define STM32_SPI_USE_SPI1 FALSE
#define STM32_SPI_USE_SPI2 FALSE
#define STM32_SPI_USE_SPI3 TRUE
#define STM32_SPI_USE_SPI4 FALSE
#define STM32_SPI_USE_SPI5 FALSE
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_SPI_SPI4_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
#define STM32_SPI_SPI4_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
#define STM32_SPI_SPI5_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
#define STM32_SPI_SPI5_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 6)
#define STM32_SPI_SPI1_DMA_PRIORITY 3
#define STM32_SPI_SPI2_DMA_PRIORITY 3
#define STM32_SPI_SPI3_DMA_PRIORITY 3
#define STM32_SPI_SPI4_DMA_PRIORITY 3
#define STM32_SPI_SPI5_DMA_PRIORITY 3
#define STM32_SPI_SPI1_IRQ_PRIORITY 10
#define STM32_SPI_SPI2_IRQ_PRIORITY 10
#define STM32_SPI_SPI3_IRQ_PRIORITY 10
#define STM32_SPI_SPI4_IRQ_PRIORITY 10
#define STM32_SPI_SPI5_IRQ_PRIORITY 10
#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
/*
* ST driver system settings.
*/
#define STM32_ST_IRQ_PRIORITY 8
#define STM32_ST_USE_TIMER 2
/*
* UART driver system settings.
*/
#define STM32_UART_USE_USART1 FALSE
#define STM32_UART_USE_USART2 FALSE
#define STM32_UART_USE_USART3 FALSE
#define STM32_UART_USE_USART6 FALSE
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
#define STM32_UART_USART6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)
#define STM32_UART_USART6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
#define STM32_UART_USART1_IRQ_PRIORITY 12
#define STM32_UART_USART2_IRQ_PRIORITY 12
#define STM32_UART_USART3_IRQ_PRIORITY 12
#define STM32_UART_USART6_IRQ_PRIORITY 12
#define STM32_UART_USART1_DMA_PRIORITY 0
#define STM32_UART_USART2_DMA_PRIORITY 0
#define STM32_UART_USART3_DMA_PRIORITY 0
#define STM32_UART_USART6_DMA_PRIORITY 0
#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
/*
* USB driver system settings.
*/
#define STM32_USB_USE_OTG1 TRUE
#define STM32_USB_OTG1_IRQ_PRIORITY 14
#define STM32_USB_OTG1_RX_FIFO_SIZE 512
#define STM32_USB_OTG_THREAD_PRIO LOWPRIO
#define STM32_USB_OTG_THREAD_STACK_SIZE 128
#define STM32_USB_OTGFIFO_FILL_BASEPRI 0
/*
* WDG driver system settings.
*/
#define STM32_WDG_USE_IWDG TRUE
#endif /* MCUCONF_H */

Wyświetl plik

@ -1,298 +0,0 @@
#include "ch.h"
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "ov2640.h"
#include "ov5640.h"
#include "pi2c.h"
#include "ssdv.h"
#include "aprs.h"
#include "radio.h"
#include "base.h"
#include <string.h>
#include "types.h"
#include "sleep.h"
#include "watchdog.h"
#include "flash.h"
static uint8_t gimage_id; // Global image ID (for all image threads)
mutex_t camera_mtx;
void encode_ssdv(uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx)
{
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE];
uint8_t pkt_base91[BASE91LEN(SSDV_PKT_SIZE-37)];
uint8_t *b;
uint32_t bi = 0;
uint8_t c = SSDV_OK;
uint16_t packet_count = 0;
uint16_t i = 0;
// Count packets
ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id);
ssdv_enc_set_buffer(&ssdv, pkt);
while(true)
{
while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
{
b = &image[bi];
uint8_t r = bi < image_len-128 ? 128 : image_len - bi;
bi += r;
if(r <= 0)
break;
ssdv_enc_feed(&ssdv, b, r);
}
if(c == SSDV_EOI || c != SSDV_OK)
break;
packet_count++;
}
TRACE_INFO("SSDV > %i packets", packet_count);
// Init SSDV (FEC at 2FSK, non FEC at APRS)
bi = 0;
ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id);
ssdv_enc_set_buffer(&ssdv, pkt);
while(true)
{
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
{
b = &image[bi];
uint8_t r = bi < image_len-128 ? 128 : image_len - bi;
bi += r;
if(r <= 0)
{
TRACE_ERROR("SSDV > Premature end of file");
break;
}
ssdv_enc_feed(&ssdv, b, r);
}
if(c == SSDV_EOI)
{
TRACE_INFO("SSDV > ssdv_enc_get_packet said EOI");
break;
} else if(c != SSDV_OK) {
TRACE_ERROR("SSDV > ssdv_enc_get_packet failed: %i", c);
return;
}
// Transmit packet
radioMSG_t msg;
msg.freq = getFrequency(&conf->frequency);
msg.power = conf->power;
switch(conf->protocol) {
case PROT_APRS_2GFSK:
case PROT_APRS_AFSK:
msg.mod = conf->protocol == PROT_APRS_AFSK ? MOD_AFSK : MOD_2GFSK;
msg.afsk_conf = &(conf->afsk_conf);
msg.gfsk_conf = &(conf->gfsk_conf);
// Deleting buffer
for(uint16_t t=0; t<256; t++)
pkt_base91[t] = 0;
base91_encode(&pkt[1], pkt_base91, sizeof(pkt)-37); // Sync byte, CRC and FEC of SSDV not transmitted
msg.bin_len = aprs_encode_experimental('I', msg.msg, msg.mod, &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91));
// Transmit on radio (keep transmitter switched on if packet spacing=0ms and it isnt the last packet being sent)
if(redudantTx) transmitOnRadio(&msg, false);
transmitOnRadio(&msg, conf->packet_spacing != 0 || i == packet_count-1);
break;
case PROT_SSDV_2FSK:
msg.mod = MOD_2FSK;
msg.fsk_conf = &(conf->fsk_conf);
memcpy(msg.msg, pkt, sizeof(pkt));
msg.bin_len = 8*sizeof(pkt);
if(redudantTx) transmitOnRadio(&msg, false);
transmitOnRadio(&msg, conf->packet_spacing != 0 || i == packet_count-1);
break;
default:
TRACE_ERROR("IMG > Unsupported protocol selected for module IMAGE");
}
// Packet spacing (delay)
if(conf->packet_spacing)
chThdSleepMilliseconds(conf->packet_spacing);
i++;
}
}
THD_FUNCTION(imgThread, arg) {
module_conf_t* conf = (module_conf_t*)arg;
systime_t time = chVTGetSystemTimeX();
while(true)
{
TRACE_INFO("IMG > Do module IMAGE cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
if(!p_sleep(&conf->sleep_conf))
{
uint32_t image_len = 0;
uint8_t *image;
// Lock camera
TRACE_INFO("IMG > Lock camera");
chMtxLock(&camera_mtx);
TRACE_INFO("IMG > Locked camera");
// Lock RADIO from producing interferences
TRACE_INFO("IMG > Lock radio");
chMtxLock(&interference_mtx);
TRACE_INFO("IMG > Locked radio");
uint8_t tries;
bool status = false;
// Detect camera
if(OV2640_isAvailable()) // OV2640 available
{
TRACE_INFO("IMG > OV2640 found");
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
{
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
do {
// Init camera
OV2640_init(&conf->ssdv_conf);
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
status = OV2640_Snapshot2RAM();
} while(!status && --tries);
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
} while(OV2640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
conf->ssdv_conf.res = RES_MAX; // Revert register
} else { // Static resolution
// Init camera
OV2640_init(&conf->ssdv_conf);
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
status = OV2640_Snapshot2RAM();
} while(!status && --tries);
}
// Switch off camera
OV2640_deinit();
// Get image
image_len = OV2640_getBuffer(&image);
TRACE_INFO("IMG > Image size: %d bytes", image_len);
} else if(OV5640_isAvailable()) { // OV5640 available
TRACE_INFO("IMG > OV5640 found");
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
{
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
do {
// Init camera
OV5640_init(&conf->ssdv_conf);
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
status = OV5640_Snapshot2RAM();
} while(!status && --tries);
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
} while(OV5640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
conf->ssdv_conf.res = RES_MAX; // Revert register
} else { // Static resolution
// Init camera
OV5640_init(&conf->ssdv_conf);
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
status = OV5640_Snapshot2RAM();
} while(!status && --tries);
}
// Switch off camera
OV5640_deinit();
// Get image
image_len = OV5640_getBuffer(&image);
TRACE_INFO("IMG > Image size: %d bytes", image_len);
} else { // Camera error
TRACE_ERROR("IMG > No camera found");
}
// Unlock radio
TRACE_INFO("IMG > Unlock radio");
chMtxUnlock(&interference_mtx);
TRACE_INFO("IMG > Unlocked radio");
// Unlock camera
TRACE_INFO("IMG > Unlock camera");
chMtxUnlock(&camera_mtx);
TRACE_INFO("IMG > Unlocked camera");
// Encode/Transmit SSDV if image sampled successfully
if(status)
{
gimage_id++;
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
}
}
time = waitForTrigger(time, &conf->trigger);
}
}
void start_image_thread(module_conf_t *conf)
{
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("IMG > Startup image thread");
chsnprintf(conf->name, sizeof(conf->name), "IMG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(6*1024), "IMG", NORMALPRIO, imgThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("IMG > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(conf);
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(1);
}
}

Wyświetl plik

@ -47,6 +47,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co
packet.max_size = 512; // TODO: replace 512 with real size
packet.mod = mod;
ax25_init(&packet);
ax25_send_header(&packet, config->callsign, config->ssid, config->path, config->preamble);
ax25_send_byte(&packet, '/'); // Report w/ timestamp, no APRS messaging. $ = NMEA raw data
@ -77,7 +78,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co
uint32_t a1 = a / 91;
uint32_t a1r = a % 91;
uint8_t gpsFix = trackPoint->gps_lock ? GSP_FIX_CURRENT : GSP_FIX_OLD;
uint8_t gpsFix = trackPoint->gps_lock ? GSP_FIX_OLD : GSP_FIX_CURRENT;
uint8_t src = NMEA_SRC_GGA;
uint8_t origin = ORIGIN_PICO;
@ -103,7 +104,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co
chsnprintf(temp, sizeof(temp), "%d", trackPoint->gps_sats);
ax25_send_string(&packet, temp);
if(trackPoint->gps_lock)
if(trackPoint->gps_lock == GPS_LOCKED) // GPS is locked
{
// TTFF (Time to first fix)
ax25_send_string(&packet, " TTFF ");
@ -112,12 +113,18 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co
ax25_send_string(&packet, "sec");
}
// GPS Loss counter
if(!trackPoint->gps_lock)
{
if(trackPoint->gps_lock == GPS_LOSS) { // No GPS lock
ax25_send_string(&packet, " GPS LOSS ");
chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter);
ax25_send_string(&packet, temp);
} else if(trackPoint->gps_lock == GPS_LOWBATT) { // GPS switched off prematurely
ax25_send_string(&packet, " GPS LOWBATT ");
chsnprintf(temp, sizeof(temp), "%d", ++loss_of_gps_counter);
ax25_send_string(&packet, temp);
} else {
loss_of_gps_counter = 0;
}
@ -140,7 +147,7 @@ uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *co
case TEL_VBAT: t = trackPoint->adc_vbat; break;
case TEL_VSOL: t = trackPoint->adc_vsol; break;
case TEL_PBAT: t = trackPoint->adc_pbat+4096; break;
case TEL_ISOL: t = trackPoint->adc_isol; break;
case TEL_RBAT: t = trackPoint->adc_rbat; break;
case TEL_HUM: t = trackPoint->air_hum; break;
case TEL_PRESS: t = trackPoint->air_press/125 - 40; break;
case TEL_TEMP: t = trackPoint->air_temp/10 + 1000; break;
@ -171,6 +178,7 @@ uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod,
packet.mod = mod;
// Encode APRS header
ax25_init(&packet);
ax25_send_header(&packet, config->callsign, config->ssid, config->path, config->preamble);
ax25_send_string(&packet, "{{");
ax25_send_byte(&packet, packetType);
@ -179,7 +187,7 @@ uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod,
for(uint16_t i=0; i<size; i++)
ax25_send_byte(&packet, data[i]);
// Send footer
// Encode footer
ax25_send_footer(&packet);
scramble(&packet);
nrzi_encode(&packet);
@ -187,6 +195,41 @@ uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod,
return packet.size;
}
/**
* Transmit custom data packet (the methods aprs_encode_data allow multiple APRS packets in a row without preable being sent)
*/
void aprs_encode_data_init(ax25_t* packet, uint8_t* message, mod_t mod)
{
packet->data = message;
packet->max_size = 8192; // TODO: replace 8192 with real size
packet->mod = mod;
// Encode APRS header
ax25_init(packet);
}
uint32_t aprs_encode_data_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size)
{
// Encode header
ax25_send_header(packet, config->callsign, config->ssid, config->path, packet->size > 0 ? 0 : config->preamble);
ax25_send_string(packet, "{{");
ax25_send_byte(packet, packetType);
// Encode message
for(uint16_t i=0; i<size; i++)
ax25_send_byte(packet, data[i]);
// Encode footer
ax25_send_footer(packet);
return packet->size;
}
uint32_t aprs_encode_data_finalize(ax25_t* packet)
{
scramble(packet);
nrzi_encode(packet);
return packet->size;
}
/**
* Transmit message packet
*/
@ -199,6 +242,7 @@ uint32_t aprs_encode_message(uint8_t* message, mod_t mod, const aprs_conf_t *con
// Encode APRS header
char temp[10];
ax25_init(&packet);
ax25_send_header(&packet, config->callsign, config->ssid, config->path, config->preamble);
ax25_send_byte(&packet, ':');
@ -212,7 +256,7 @@ uint32_t aprs_encode_message(uint8_t* message, mod_t mod, const aprs_conf_t *con
chsnprintf(temp, sizeof(temp), "%d", ++msg_id);
ax25_send_string(&packet, temp);
// Send footer
// Encode footer
ax25_send_footer(&packet);
scramble(&packet);
nrzi_encode(&packet);
@ -231,6 +275,7 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const
packet.max_size = 512; // TODO: replace 512 with real size
packet.mod = mod;
ax25_init(&packet);
ax25_send_header(&packet, config->callsign, config->ssid, config->path, config->preamble); // Header
ax25_send_byte(&packet, ':'); // Message flag
@ -259,7 +304,7 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const
case TEL_VBAT: ax25_send_string(&packet, "Vbat"); break;
case TEL_VSOL: ax25_send_string(&packet, "Vsol"); break;
case TEL_PBAT: ax25_send_string(&packet, "Pbat"); break;
case TEL_ISOL: ax25_send_string(&packet, "Isol"); break;
case TEL_RBAT: ax25_send_string(&packet, "Rbat"); break;
case TEL_HUM: ax25_send_string(&packet, "Humidity"); break;
case TEL_PRESS: ax25_send_string(&packet, "Airpressure"); break;
case TEL_TEMP: ax25_send_string(&packet, "Temperature"); break;
@ -292,8 +337,8 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const
ax25_send_string(&packet, "W");
break;
case TEL_ISOL:
ax25_send_string(&packet, "A");
case TEL_RBAT:
ax25_send_string(&packet, "Ohm");
break;
case TEL_HUM:
@ -325,10 +370,9 @@ uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const
ax25_send_string(&packet, "0,1,0");
break;
case TEL_ISOL:
case TEL_VBAT:
case TEL_VSOL:
case TEL_RBAT:
ax25_send_string(&packet, "0,.001,0");
break;

Wyświetl plik

@ -21,6 +21,7 @@
#include "config.h"
#include "si4464.h"
#include "tracking.h"
#include "ax25.h"
#define GSP_FIX_OLD 0x0
#define GSP_FIX_CURRENT 0x1
@ -51,7 +52,11 @@
uint32_t aprs_encode_position(uint8_t* message, mod_t mod, const aprs_conf_t *config, trackPoint_t *trackPoint);
uint32_t aprs_encode_telemetry_configuration(uint8_t* message, mod_t mod, const aprs_conf_t *config, const telemetry_conf_t type);
uint32_t aprs_encode_message(uint8_t* message, mod_t mod, const aprs_conf_t *config, const char *receiver, const char *text);
uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod, const aprs_conf_t *config, uint8_t *image, size_t size);
uint32_t aprs_encode_experimental(char packetType, uint8_t* message, mod_t mod, const aprs_conf_t *config, uint8_t *data, size_t size);
void aprs_encode_data_init(ax25_t* packet, uint8_t* message, mod_t mod);
uint32_t aprs_encode_data_encodePacket(ax25_t* packet, char packetType, const aprs_conf_t *config, uint8_t *data, size_t size);
uint32_t aprs_encode_data_finalize(ax25_t* packet);
#endif

Wyświetl plik

@ -113,11 +113,15 @@ void ax25_send_string(ax25_t *packet, const char *string)
}
}
void ax25_init(ax25_t *packet)
{
packet->size = 0;
}
void ax25_send_header(ax25_t *packet, const char *callsign, uint8_t ssid, const char *path, uint16_t preamble)
{
uint8_t i, j;
uint8_t tmp[8];
packet->size = 0;
packet->ones_in_a_row = 0;
packet->crc = 0xffff;

Wyświetl plik

@ -36,6 +36,7 @@ typedef struct {
mod_t mod; // Modulation type (MOD_AFSK or MOD_2GFSK)
} ax25_t;
void ax25_init(ax25_t *packet);
void ax25_send_header(ax25_t *packet, const char *callsign, uint8_t ssid, const char *path, uint16_t preamble);
void ax25_send_path(ax25_t *packet, const char *callsign, uint8_t ssid, bool last);
void ax25_send_byte(ax25_t *packet, char byte);

Wyświetl plik

@ -1,7 +1,7 @@
/* SSDV - Slow Scan Digital Video */
/*=======================================================================*/
/* Copyright 2011-2012 Philip Heron <phil@sanslogic.co.uk */
/* Copyright 2011-2016 Philip Heron <phil@sanslogic.co.uk> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
@ -46,6 +46,11 @@ static const uint8_t const sos[10] = {
0x03,0x01,0x00,0x02,0x11,0x03,0x11,0x00,0x3F,0x00,
};
/* Quantisation table scaling factors for each quality level 0-7 */
static const uint16_t const dqt_scales[8] = {
5000, 357, 172, 116, 100, 58, 28, 0
};
/* Quantisation tables */
static const uint8_t const std_dqt0[65] = {
0x00,0x10,0x0C,0x0C,0x0E,0x0C,0x0A,0x10,0x0E,0x0E,0x0E,0x12,0x12,0x10,0x14,0x18,
@ -136,6 +141,61 @@ static char *strbits(uint32_t value, uint8_t bits)
}
*/
static void load_standard_dqt(uint8_t *dst, const uint8_t *table, uint8_t quality)
{
int i;
uint16_t scale_factor;
uint32_t temp;
/* Copy the table ID */
*dst++ = *table++;
/* Load the scaling factor */
if(quality > 7) quality = 7;
scale_factor = dqt_scales[quality];
/* Copy the remaining 64 coefficients, while applying the scaling factor */
for(i = 0; i < 64; i++)
{
temp = *table++;
temp = (temp * scale_factor + 50) / 100;
/* limit the values to the valid range */
if(temp == 0) temp = 1;
if(temp > 255) temp = 255;
*dst++ = temp;
}
}
static void *sload_standard_dqt(ssdv_t *s, const uint8_t *table, uint8_t quality)
{
uint8_t *r;
/* DQT is 65 bytes long, ensure there is space */
if(s->stbl_len + 65 > TBL_LEN + HBUFF_LEN) return(NULL);
r = &s->stbls[s->stbl_len];
load_standard_dqt(r, table, quality);
s->stbl_len += 65;
return(r);
}
static void *dload_standard_dqt(ssdv_t *s, const uint8_t *table, uint8_t quality)
{
uint8_t *r;
/* DQT is 65 bytes long, ensure there is space */
if(s->dtbl_len + 65 > TBL_LEN + HBUFF_LEN) return(NULL);
r = &s->dtbls[s->dtbl_len];
load_standard_dqt(r, table, quality);
s->dtbl_len += 65;
return(r);
}
static void *stblcpy(ssdv_t *s, const void *src, size_t n)
{
void *r;
@ -607,7 +667,7 @@ static char ssdv_have_marker(ssdv_t *s)
case J_SOF2:
/* Don't do progressive images! */
TRACE_ERROR("SSDV > Progressive images not supported");
TRACE_ERROR("SSDV > Error: Progressive images not supported");
return(SSDV_ERROR);
case J_EOI:
@ -659,28 +719,28 @@ static char ssdv_have_marker_data(ssdv_t *s)
/* The image must have a precision of 8 */
if(d[0] != 8)
{
TRACE_ERROR("SSDV > The image must have a precision of 8");
TRACE_ERROR("SSDV > Error: The image must have a precision of 8");
return(SSDV_ERROR);
}
/* The image must have 3 components (Y'Cb'Cr) */
if(d[5] != 3)
{
TRACE_ERROR("SSDV > The image must have 3 components");
TRACE_ERROR("SSDV > Error: The image must have 3 components");
return(SSDV_ERROR);
}
/* Maximum image is 4080x4080 */
if(s->width > 4080 || s->height > 4080)
{
TRACE_ERROR("SSDV > The image is too big. Maximum resolution is 4080x4080");
TRACE_ERROR("SSDV > Error: The image is too big. Maximum resolution is 4080x4080");
return(SSDV_ERROR);
}
/* The image dimensions must be a multiple of 16 */
if((s->width & 0x0F) || (s->height & 0x0F))
{
TRACE_ERROR("SSDV > The image dimensions must be a multiple of 16");
TRACE_ERROR("SSDV > Error: The image dimensions must be a multiple of 16");
return(SSDV_ERROR);
}
@ -691,7 +751,7 @@ static char ssdv_have_marker_data(ssdv_t *s)
uint8_t *dq = &d[i * 3 + 6];
if(dq[0] != i + 1)
{
TRACE_ERROR("SSDV > Components are not in order in the SOF0 header");
TRACE_ERROR("SSDV > Error: Components are not in order in the SOF0 header");
return(SSDV_ERROR);
}
@ -707,13 +767,13 @@ static char ssdv_have_marker_data(ssdv_t *s)
case 0x21: s->mcu_mode = 2; s->ycparts = 2; break;
case 0x11: s->mcu_mode = 3; s->ycparts = 1; break;
default:
TRACE_ERROR("SSDV > Component 1 sampling factor is not supported");
TRACE_ERROR("SSDV > Error: Component 1 sampling factor is not supported");
return(SSDV_ERROR);
}
}
else if(dq[0] != 1 && dq[1] != 0x11)
{
TRACE_ERROR("SSDV > Component %i sampling factor must be 1x1", dq[0]);
TRACE_ERROR("SSDV > Error: Component %i sampling factor must be 1x1", dq[0]);
return(SSDV_ERROR);
}
}
@ -731,7 +791,7 @@ static char ssdv_have_marker_data(ssdv_t *s)
if(l > 0xFFFF)
{
TRACE_ERROR("SSDV > Maximum number of MCU blocks is 65535");
TRACE_ERROR("SSDV > Error: Maximum number of MCU blocks is 65535");
return(SSDV_ERROR);
}
@ -745,7 +805,7 @@ static char ssdv_have_marker_data(ssdv_t *s)
/* The image must have 3 components (Y'Cb'Cr) */
if(d[0] != 3)
{
TRACE_ERROR("SSDV > The image must have 3 components");
TRACE_ERROR("SSDV > Error: The image must have 3 components");
return(SSDV_ERROR);
}
@ -754,7 +814,7 @@ static char ssdv_have_marker_data(ssdv_t *s)
uint8_t *dh = &d[i * 2 + 1];
if(dh[0] != i + 1)
{
TRACE_ERROR("SSDV > Components are not in order in the SOF0 header");
TRACE_ERROR("SSDV > Error: Components are not in order in the SOF0 header");
return(SSDV_ERROR);
}
@ -767,14 +827,14 @@ static char ssdv_have_marker_data(ssdv_t *s)
/* Verify all of the DQT and DHT tables where loaded */
if(!s->sdqt[0] || !s->sdqt[1])
{
TRACE_ERROR("SSDV > The image is missing one or more DQT tables");
TRACE_ERROR("SSDV > Error: The image is missing one or more DQT tables");
return(SSDV_ERROR);
}
if(!s->sdht[0][0] || !s->sdht[0][1] ||
!s->sdht[1][0] || !s->sdht[1][1])
{
TRACE_ERROR("SSDV > The image is missing one or more DHT tables");
TRACE_ERROR("SSDV > Error: The image is missing one or more DHT tables");
return(SSDV_ERROR);
}
@ -824,7 +884,7 @@ static char ssdv_have_marker_data(ssdv_t *s)
case J_DRI:
s->dri = (d[0] << 8) + d[1];
TRACE_ERROR("SSDV > Reset interval: %i blocks", s->dri);
TRACE_INFO("SSDV > Reset interval: %i blocks", s->dri);
break;
}
@ -832,18 +892,23 @@ static char ssdv_have_marker_data(ssdv_t *s)
return(SSDV_OK);
}
char ssdv_enc_init(ssdv_t *s, uint8_t type, char *callsign, uint8_t image_id)
char ssdv_enc_init(ssdv_t *s, uint8_t type, char *callsign, uint8_t image_id, int8_t quality)
{
/* Limit the quality level */
if(quality < 0) quality = 0;
if(quality > 7) quality = 7;
memset(s, 0, sizeof(ssdv_t));
s->image_id = image_id;
s->callsign = encode_callsign(callsign);
s->mode = S_ENCODING;
s->type = type;
s->quality = quality;
ssdv_set_packet_conf(s);
/* Prepare the output JPEG tables */
s->ddqt[0] = dtblcpy(s, std_dqt0, sizeof(std_dqt0));
s->ddqt[1] = dtblcpy(s, std_dqt1, sizeof(std_dqt1));
s->ddqt[0] = dload_standard_dqt(s, std_dqt0, s->quality);
s->ddqt[1] = dload_standard_dqt(s, std_dqt1, s->quality);
s->ddht[0][0] = dtblcpy(s, std_dht00, sizeof(std_dht00));
s->ddht[0][1] = dtblcpy(s, std_dht01, sizeof(std_dht01));
s->ddht[1][0] = dtblcpy(s, std_dht10, sizeof(std_dht10));
@ -973,6 +1038,7 @@ char ssdv_enc_get_packet(ssdv_t *s)
s->out[9] = s->width >> 4; /* Width / 16 */
s->out[10] = s->height >> 4; /* Height / 16 */
s->out[11] = 0x00;
s->out[11] |= ((s->quality - 4) & 7) << 3; /* Quality level */
s->out[11] |= (r == SSDV_EOI ? 1 : 0) << 2; /* EOI flag (1 bit) */
s->out[11] |= s->mcu_mode & 0x03; /* MCU mode (2 bits) */
s->out[12] = mcu_offset; /* Next MCU offset */
@ -1020,7 +1086,7 @@ char ssdv_enc_get_packet(ssdv_t *s)
return(SSDV_FEED_ME);
}
char ssdv_enc_feed(ssdv_t *s, uint8_t *buffer, size_t length)
char ssdv_enc_feed(ssdv_t *s, const uint8_t *buffer, size_t length)
{
s->inp = buffer;
s->in_len = length;
@ -1045,8 +1111,8 @@ static void ssdv_out_headers(ssdv_t *s)
ssdv_write_marker(s, J_SOI, 0, 0);
ssdv_write_marker(s, J_APP0, 14, app0);
ssdv_write_marker(s, J_DQT, 65, std_dqt0); /* DQT Luminance */
ssdv_write_marker(s, J_DQT, 65, std_dqt1); /* DQT Chrominance */
ssdv_write_marker(s, J_DQT, 65, s->ddqt[0]); /* DQT Luminance */
ssdv_write_marker(s, J_DQT, 65, s->ddqt[1]); /* DQT Chrominance */
/* Build SOF0 header */
b[0] = 8; /* Precision */
@ -1125,16 +1191,12 @@ char ssdv_dec_init(ssdv_t *s)
s->mode = S_DECODING;
/* Prepare the source JPEG tables */
s->sdqt[0] = stblcpy(s, std_dqt0, sizeof(std_dqt0));
s->sdqt[1] = stblcpy(s, std_dqt1, sizeof(std_dqt1));
s->sdht[0][0] = stblcpy(s, std_dht00, sizeof(std_dht00));
s->sdht[0][1] = stblcpy(s, std_dht01, sizeof(std_dht01));
s->sdht[1][0] = stblcpy(s, std_dht10, sizeof(std_dht10));
s->sdht[1][1] = stblcpy(s, std_dht11, sizeof(std_dht11));
/* Prepare the output JPEG tables */
s->ddqt[0] = dtblcpy(s, std_dqt0, sizeof(std_dqt0));
s->ddqt[1] = dtblcpy(s, std_dqt1, sizeof(std_dqt1));
s->ddht[0][0] = dtblcpy(s, std_dht00, sizeof(std_dht00));
s->ddht[0][1] = dtblcpy(s, std_dht01, sizeof(std_dht01));
s->ddht[1][0] = dtblcpy(s, std_dht10, sizeof(std_dht10));
@ -1183,12 +1245,19 @@ char ssdv_dec_feed(ssdv_t *s, uint8_t *packet)
s->width = packet[9] << 4;
s->height = packet[10] << 4;
s->mcu_count = packet[9] * packet[10];
s->quality = ((packet[11] >> 3) & 7) ^ 4;
s->mcu_mode = packet[11] & 0x03;
/* Configure the payload size and CRC position */
ssdv_set_packet_conf(s);
switch(s->mcu_mode)
/* Generate the DQT tables */
s->sdqt[0] = sload_standard_dqt(s, std_dqt0, s->quality);
s->sdqt[1] = sload_standard_dqt(s, std_dqt1, s->quality);
s->ddqt[0] = dload_standard_dqt(s, std_dqt0, s->quality);
s->ddqt[1] = dload_standard_dqt(s, std_dqt1, s->quality);
switch(s->mcu_mode & 3)
{
case 0: factor = "2x2"; s->ycparts = 4; break;
case 1: factor = "1x2"; s->ycparts = 2; s->mcu_count *= 2; break;
@ -1202,6 +1271,7 @@ char ssdv_dec_feed(ssdv_t *s, uint8_t *packet)
TRACE_INFO("SSDV > Resolution: %ix%i", s->width, s->height);
TRACE_INFO("SSDV > MCU blocks: %i", s->mcu_count);
TRACE_INFO("SSDV > Sampling factor: %s", factor);
TRACE_INFO("SSDV > Quality level: %d", s->quality);
/* Output JPEG headers and enable byte stuffing */
ssdv_out_headers(s);
@ -1301,19 +1371,50 @@ char ssdv_dec_is_packet(uint8_t *packet, int *errors)
memcpy(pkt, packet, SSDV_PKT_SIZE);
pkt[0] = 0x55;
type = SSDV_TYPE_INVALID;
if(pkt[1] == 0x66 + SSDV_TYPE_NOFEC)
{
type = SSDV_TYPE_NOFEC;
/* Test for a valid NOFEC packet */
pkt_size_payload = SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CRC;
pkt_size_crcdata = SSDV_PKT_SIZE_HEADER + pkt_size_payload - 1;
/* No FEC scan */
if(errors) *errors = 0;
}
else
{
type = SSDV_TYPE_NORMAL;
/* Test the checksum */
x = crc32(&pkt[1], pkt_size_crcdata);
i = 1 + pkt_size_crcdata;
if(x == (pkt[i + 3] | (pkt[i + 2] << 8) | (pkt[i + 1] << 16) | (pkt[i] << 24)))
{
/* Valid, set the type and continue */
type = SSDV_TYPE_NOFEC;
}
}
else if(pkt[1] == 0x66 + SSDV_TYPE_NORMAL)
{
/* Test for a valid NORMAL packet */
pkt_size_payload = SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CRC - SSDV_PKT_SIZE_RSCODES;
pkt_size_crcdata = SSDV_PKT_SIZE_HEADER + pkt_size_payload - 1;
/* No FEC scan */
if(errors) *errors = 0;
/* Test the checksum */
x = crc32(&pkt[1], pkt_size_crcdata);
i = 1 + pkt_size_crcdata;
if(x == (pkt[i + 3] | (pkt[i + 2] << 8) | (pkt[i + 1] << 16) | (pkt[i] << 24)))
{
/* Valid, set the type and continue */
type = SSDV_TYPE_NORMAL;
}
}
if(type == SSDV_TYPE_INVALID)
{
/* Test for a valid NORMAL packet with correctable errors */
pkt_size_payload = SSDV_PKT_SIZE - SSDV_PKT_SIZE_HEADER - SSDV_PKT_SIZE_CRC - SSDV_PKT_SIZE_RSCODES;
pkt_size_crcdata = SSDV_PKT_SIZE_HEADER + pkt_size_payload - 1;
@ -1323,6 +1424,22 @@ char ssdv_dec_is_packet(uint8_t *packet, int *errors)
if(i < 0) return(-1); /* Reed-solomon decoder failed */
if(errors) *errors = i;
/* Test the checksum */
x = crc32(&pkt[1], pkt_size_crcdata);
i = 1 + pkt_size_crcdata;
if(x == (pkt[i + 3] | (pkt[i + 2] << 8) | (pkt[i + 1] << 16) | (pkt[i] << 24)))
{
/* Valid, set the type and continue */
type = SSDV_TYPE_NORMAL;
}
}
if(type == SSDV_TYPE_INVALID)
{
/* All attempts to read the packet have failed */
return(-1);
}
/* Sanity checks */
@ -1336,15 +1453,6 @@ char ssdv_dec_is_packet(uint8_t *packet, int *errors)
if(p.mcu_offset >= pkt_size_payload) return(-1);
}
/* Test the checksum */
x = crc32(&pkt[1], pkt_size_crcdata);
i = 1 + pkt_size_crcdata;
if(pkt[i++] != ((x >> 24) & 0xFF)) return(-1);
if(pkt[i++] != ((x >> 16) & 0xFF)) return(-1);
if(pkt[i++] != ((x >> 8) & 0xFF)) return(-1);
if(pkt[i++] != (x & 0xFF)) return(-1);
/* Appears to be a valid packet! Copy it back */
memcpy(packet, pkt, SSDV_PKT_SIZE);
@ -1361,6 +1469,7 @@ void ssdv_dec_header(ssdv_packet_info_t *info, uint8_t *packet)
info->width = packet[9] << 4;
info->height = packet[10] << 4;
info->eoi = (packet[11] >> 2) & 1;
info->quality = ((packet[11] >> 3) & 7) ^ 4;
info->mcu_mode = packet[11] & 0x03;
info->mcu_offset = packet[12];
info->mcu_id = (packet[13] << 8) | packet[14];

Wyświetl plik

@ -1,7 +1,7 @@
/* SSDV - Slow Scan Digital Video */
/*=======================================================================*/
/* Copyright 2011-2012 Philip Heron <phil@sanslogic.co.uk */
/* Copyright 2011-2016 Philip Heron <phil@sanslogic.co.uk> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
@ -42,8 +42,9 @@ extern "C" {
#define SSDV_MAX_CALLSIGN (6) /* Maximum number of characters in a callsign */
#define SSDV_TYPE_NORMAL (0)
#define SSDV_TYPE_NOFEC (1)
#define SSDV_TYPE_INVALID (0xFF)
#define SSDV_TYPE_NORMAL (0x00)
#define SSDV_TYPE_NOFEC (0x01)
typedef struct
{
@ -62,11 +63,12 @@ typedef struct
uint8_t mcu_mode; /* 0 = 2x2, 1 = 2x1, 2 = 1x2, 3 = 1x1 */
uint16_t mcu_id;
uint16_t mcu_count;
uint8_t quality; /* JPEG quality level for encoding, 0-7 */
uint16_t packet_mcu_id;
uint8_t packet_mcu_offset;
/* Source buffer */
uint8_t *inp; /* Pointer to next input byte */
const uint8_t *inp;/* Pointer to next input byte */
size_t in_len; /* Number of input bytes remaining */
size_t in_skip; /* Number of input bytes to skip */
@ -134,6 +136,7 @@ typedef struct {
uint16_t width;
uint16_t height;
uint8_t eoi;
uint8_t quality;
uint16_t mcu_mode;
uint8_t mcu_offset;
uint16_t mcu_id;
@ -141,10 +144,10 @@ typedef struct {
} ssdv_packet_info_t;
/* Encoding */
extern char ssdv_enc_init(ssdv_t *s, uint8_t type, char *callsign, uint8_t image_id);
extern char ssdv_enc_init(ssdv_t *s, uint8_t type, char *callsign, uint8_t image_id, int8_t quality);
extern char ssdv_enc_set_buffer(ssdv_t *s, uint8_t *buffer);
extern char ssdv_enc_get_packet(ssdv_t *s);
extern char ssdv_enc_feed(ssdv_t *s, uint8_t *buffer, size_t length);
extern char ssdv_enc_feed(ssdv_t *s, const uint8_t *buffer, size_t length);
/* Decoding */
extern char ssdv_dec_init(ssdv_t *s);

Wyświetl plik

@ -9,7 +9,7 @@
#include "padc.h"
#include <string.h>
#define PLAYBACK_RATE ((STM32_PCLK1) / 250) /* Samples per second (48Mhz / 250 = 192kHz) */
#define PLAYBACK_RATE ((STM32_PCLK1) / 500) /* Samples per second (48Mhz / 250 = 192kHz) */
#define BAUD_RATE 1200 /* APRS AFSK baudrate */
#define SAMPLES_PER_BAUD (PLAYBACK_RATE / BAUD_RATE) /* Samples per baud (192kHz / 1200baud = 160samp/baud) */
#define PHASE_DELTA_1200 (((2 * 1200) << 16) / PLAYBACK_RATE) /* Delta-phase per sample for 1200Hz tone */
@ -22,15 +22,12 @@ static uint32_t phase; // Fixed point 9.7 (2PI = TABLE_SIZE)
static uint32_t packet_pos; // Next bit to be sent out
static uint32_t current_sample_in_baud; // 1 bit = SAMPLES_PER_BAUD samples
static uint8_t current_byte;
static radioMSG_t *tim_msg;
static uint32_t gfsk_bit;
static radioMSG_t tim_msg;
static uint8_t txs; // Serial maschine state
static uint8_t txc; // Current byte
static uint32_t txi; // Bitcounter of current byte
static uint32_t txj; // Bytecounter
static radioMSG_t *fsk_msg; // Current message
static virtual_timer_t vt; // UART timer
static const char *getModulation(uint8_t key) {
const char *val[] = {"unknown", "OOK", "2FSK", "2GFSK", "AFSK"};
@ -38,15 +35,17 @@ static const char *getModulation(uint8_t key) {
};
void initAFSK(radioMSG_t *msg) {
// Initialize radio and tune
(void)msg;
// Initialize radio
Si4464_Init();
setModemAFSK();
radioTune(msg->freq, 0, msg->power, 0);
active_mod = MOD_AFSK;
}
void sendAFSK(radioMSG_t *msg) {
(void)msg;
// Initialize variables for timer
tim_msg = msg;
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
phase_delta = PHASE_DELTA_1200;
phase = 0;
@ -54,12 +53,13 @@ void sendAFSK(radioMSG_t *msg) {
current_sample_in_baud = 0;
current_byte = 0;
// Tune
radioTune(msg->freq, 0, msg->power, 0);
// Initialize timer
uint32_t initial_interval = 250;
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = initial_interval;
TIM7->PSC = 1;
TIM7->ARR = 500;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
@ -69,48 +69,81 @@ void sendAFSK(radioMSG_t *msg) {
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
shutdownRadio();
}
void init2GFSK(radioMSG_t *msg) {
// Initialize radio and tune
// Initialize radio
Si4464_Init();
setModem2GFSK(msg->gfsk_conf);
radioTune(msg->freq, 0, msg->power, 0);
chThdSleepMilliseconds(30);
active_mod = MOD_2GFSK;
}
thread_t *feeder_thd = NULL;
/*
* TODO: I'd suggest re-working the FIFO handler system.
* Will give it some thought.
* Meanwhile there are a few changes that are worth testing.
*/
static THD_WORKING_AREA(si_fifo_feeder_wa, 1024);
THD_FUNCTION(si_fifo_feeder_thd, arg)
{
(void)arg;
uint16_t c = 64;
uint16_t all = (tim_msg.bin_len+7)/8;
chRegSetThreadName("radio_tx_feeder");
// Initial FIFO fill
Si4464_writeFIFO(tim_msg.msg, c);
// Start transmission
radioTune(tim_msg.freq, 0, tim_msg.power, all);
while(c < all) { // Do while bytes not written into FIFO completely
//chThdSuspendS(&feeder_ref); // Suspend until interrupt resumes it
// Determine free memory in Si4464-FIFO
uint8_t more = Si4464_freeFIFO();
if(more > all-c) {
if((more = all-c) == 0) // Calculate remainder to send
break; // End if nothing left
}
Si4464_writeFIFO(&tim_msg.msg[c], more); // Write into FIFO
c += more;
chThdSleepMilliseconds(5);
}
// Shutdown radio (and wait for Si4464 to finish transmission)
shutdownRadio();
chThdExit(MSG_OK);
}
void send2GFSK(radioMSG_t *msg) {
// Initialize variables for timer
tim_msg = msg;
gfsk_bit = 0;
current_byte = 0;
// Copy data
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
// Initialize variables for timer
uint32_t initial_interval = STM32_PCLK1 / msg->gfsk_conf->speed;
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = initial_interval;
TIM7->PSC = 1;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start/re-start FIFO feeder
feeder_thd = chThdCreateStatic(si_fifo_feeder_wa, sizeof(si_fifo_feeder_wa), HIGHPRIO+1, si_fifo_feeder_thd, NULL);
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
// Wait for the transmitter to start (because it is used as mutex)
while(Si4464_getState() != SI4464_STATE_TX)
chThdSleepMilliseconds(1);
}
/**
* Fast interrupt handler for AFSK and 2GFSK modulation. It has has the
* highest priority in order to provide an accurate low jitter modulation.
* Fast interrupt handler for AFSK modulation. It has has the highest priority
* in order to provide an accurate low jitter modulation.
*/
CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
{
if(tim_msg->mod == MOD_AFSK) {
if(active_mod == MOD_AFSK) // AFSK
{
if(packet_pos == tim_msg->bin_len) { // Packet transmission finished
if(packet_pos == tim_msg.bin_len) { // Packet transmission finished
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
@ -118,7 +151,7 @@ CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
if(current_sample_in_baud == 0) {
if((packet_pos & 7) == 0) { // Load up next byte
current_byte = tim_msg->msg[packet_pos >> 3];
current_byte = tim_msg.msg[packet_pos >> 3];
} else { // Load up next bit
current_byte = current_byte / 2;
}
@ -128,7 +161,7 @@ CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
phase_delta = (current_byte & 1) ? PHASE_DELTA_1200 : PHASE_DELTA_2200;
phase += phase_delta; // Add delta-phase (delta-phase tone dependent)
RADIO_MOD_GPIO((phase >> 16) & 1); // Set modulaton pin (connected to Si4464)
RADIO_WRITE_GPIO((phase >> 16) & 1); // Set modulaton pin (connected to Si4464)
current_sample_in_baud++;
@ -137,127 +170,130 @@ CH_FAST_IRQ_HANDLER(STM32_TIM7_HANDLER)
packet_pos++;
}
} else if(tim_msg->mod == MOD_2GFSK) {
} else { // 2FSK
if(gfsk_bit >= tim_msg->bin_len) { // Packet transmission finished
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
switch(txs)
{
case 6: // TX-delay
txj++;
if(txj > (uint32_t)(tim_msg.fsk_conf->predelay * tim_msg.fsk_conf->baud / 1000)) {
txj = 0;
txs = 7;
}
break;
case 7: // Transmit a single char
if(txj < tim_msg.bin_len/8) {
txc = tim_msg.msg[txj]; // Select char
txj++;
RADIO_WRITE_GPIO(LOW); // Start Bit (Synchronizing)
txi = 0;
txs = 8;
} else { // Finished to transmit string
RADIO_WRITE_GPIO(HIGH);
TIM7->CR1 &= ~STM32_TIM_CR1_CEN; // Disable timer
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
return;
}
break;
case 8:
if(txi < tim_msg.fsk_conf->bits) {
txi++;
RADIO_WRITE_GPIO(txc & 1);
txc = txc >> 1;
} else {
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txi = 0;
txs = 9;
}
break;
case 9:
if(tim_msg.fsk_conf->stopbits == 2)
RADIO_WRITE_GPIO(HIGH); // Stop Bit
txs = 7;
}
if((gfsk_bit & 7) == 0) { // Load up next byte
current_byte = tim_msg->msg[gfsk_bit >> 3];
} else {
current_byte = current_byte / 2; // Load next bit
}
RADIO_MOD_GPIO(current_byte & 0x1);
gfsk_bit++;
palToggleLine(LINE_IO_LED1);
}
palToggleLine(LINE_IO_LED1);
TIM7->SR &= ~STM32_TIM_SR_UIF; // Reset interrupt flag
}
void initOOK(radioMSG_t *msg) {
// Initialize radio and tune
(void)msg;
// Initialize radio
Si4464_Init();
setModemOOK();
radioTune(msg->freq, 0, msg->power, 0);
active_mod = MOD_OOK;
}
/**
* Transmits binary OOK message. One bit = 20ms (1: TONE, 0: NO TONE)
*/
void sendOOK(radioMSG_t *msg) {
// Tune
radioTune(msg->freq, 0, msg->power, 0);
// Transmit data
uint32_t bit = 0;
systime_t time = chVTGetSystemTimeX();
while(bit < msg->bin_len) {
RADIO_MOD_GPIO((msg->msg[bit/8] >> (bit%8)) & 0x1);
RADIO_WRITE_GPIO((msg->msg[bit/8] >> (bit%8)) & 0x1);
bit++;
time = chThdSleepUntilWindowed(time, time + MS2ST(1200 / msg->ook_conf->speed));
}
}
static void serial_cb(void *arg) {
(void)arg;
switch(txs)
{
case 6: // TX-delay
txj++;
if(txj > (uint32_t)(fsk_msg->fsk_conf->predelay * fsk_msg->fsk_conf->baud / 1000)) {
txj = 0;
txs = 7;
}
break;
case 7: // Transmit a single char
if(txj < fsk_msg->bin_len/8) {
txc = fsk_msg->msg[txj]; // Select char
txj++;
RADIO_MOD_GPIO(LOW); // Start Bit (Synchronizing)
txi = 0;
txs = 8;
} else {
txj = 0;
txs = 0; // Finished to transmit string
RADIO_MOD_GPIO(HIGH);
}
break;
case 8:
if(txi < fsk_msg->fsk_conf->bits) {
txi++;
RADIO_MOD_GPIO(txc & 1);
txc = txc >> 1;
} else {
RADIO_MOD_GPIO(HIGH); // Stop Bit
txi = 0;
txs = 9;
}
break;
case 9:
if(fsk_msg->fsk_conf->stopbits == 2)
RADIO_MOD_GPIO(HIGH); // Stop Bit
txs = 7;
}
// Reload timer
if(txs) {
chSysLockFromISR();
uint32_t delay = US2ST(1000000/fsk_msg->fsk_conf->baud);
chVTSetI(&vt, delay, serial_cb, NULL);
chSysUnlockFromISR();
}
shutdownRadio();
}
void init2FSK(radioMSG_t *msg) {
// Initialize virtual timer
chVTObjectInit(&vt);
(void)msg;
// Initialize radio and tune
Si4464_Init();
setModem2FSK();
radioTune(msg->freq, msg->fsk_conf->shift, msg->power, 0);
}
void send2FSK(radioMSG_t *msg) {
// Prepare serial machine states
// Initialize variables for timer
memcpy(&tim_msg, msg, sizeof(radioMSG_t));
txs = 6;
txc = 0;
txi = 0;
txj = 0;
fsk_msg = msg;
// Modulate
chVTSet(&vt, 1, serial_cb, NULL); // Start timer
while(txs)
chThdSleepMilliseconds(1); // Wait for routine to finish
// Tune
radioTune(msg->freq, msg->fsk_conf->shift, msg->power, 0);
// Initialize timer
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
nvicEnableVector(TIM7_IRQn, 1);
TIM7->ARR = STM32_PCLK1 / 16 / msg->fsk_conf->baud; // FIXME: 5625000 should be actually STM32_PCLK1
TIM7->PSC = 15;
TIM7->CR1 &= ~STM32_TIM_CR1_ARPE;
TIM7->DIER |= STM32_TIM_DIER_UIE;
// Start timer
TIM7->CR1 |= STM32_TIM_CR1_CEN;
// Block execution while timer is running
while(TIM7->CR1 & STM32_TIM_CR1_CEN)
chThdSleepMilliseconds(10);
shutdownRadio();
}
void shutdownRadio(void)
{
// Wait for PH to finish transmission for 2GFSK
while(active_mod == MOD_2GFSK && Si4464_getState() == SI4464_STATE_TX)
chThdSleepMilliseconds(5);
Si4464_shutdown();
active_mod = MOD_NOT_SET;
}
/**
@ -312,55 +348,60 @@ uint32_t getAPRSRegionFrequency(void) {
/**
* Sends radio message into message box. This method will return false if message box is full.
*/
bool transmitOnRadio(radioMSG_t *msg, bool shutdown) {
// Lock radio
chMtxLock(&radio_mtx);
if(inRadioBand(msg->freq)) { // Frequency in radio radio band
// Lock interference mutex
chMtxLock(&interference_mtx);
TRACE_INFO( "RAD > Transmit %d.%03d MHz, Pwr %d, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power,
getModulation(msg->mod), msg->bin_len
);
switch(msg->mod) {
case MOD_2FSK:
if(active_mod != msg->mod)
init2FSK(msg);
send2FSK(msg);
break;
case MOD_2GFSK:
if(active_mod != msg->mod)
init2GFSK(msg);
send2GFSK(msg);
break;
case MOD_AFSK:
if(active_mod != msg->mod)
initAFSK(msg);
sendAFSK(msg);
break;
case MOD_OOK:
if(active_mod != msg->mod)
initOOK(msg);
sendOOK(msg);
break;
case MOD_NOT_SET:
TRACE_ERROR("RAD > Modulation not set");
break;
}
if(shutdown)
bool transmitOnRadio(radioMSG_t *msg, bool shutdown)
{
(void)shutdown;
if(inRadioBand(msg->freq)) // Frequency in radio radio band
{
if(inRadioBand(msg->freq)) // Frequency in radio radio band
{
radioShutdown(); // Shutdown radio for reinitialization
active_mod = MOD_NOT_SET;
} else {
active_mod = msg->mod;
}
lockRadio(); // Lock radio
chMtxUnlock(&interference_mtx); // Heavy interference finished (HF)
TRACE_INFO( "RAD > Transmit %d.%03d MHz, Pwr %d, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power,
getModulation(msg->mod), msg->bin_len
);
switch(msg->mod)
{
case MOD_2FSK:
if(active_mod != msg->mod)
init2FSK(msg);
send2FSK(msg);
break;
case MOD_2GFSK:
if(active_mod != msg->mod)
init2GFSK(msg);
send2GFSK(msg);
break;
case MOD_AFSK:
if(active_mod != msg->mod)
initAFSK(msg);
sendAFSK(msg);
break;
case MOD_OOK:
if(active_mod != msg->mod)
initOOK(msg);
sendOOK(msg);
break;
case MOD_NOT_SET:
TRACE_ERROR("RAD > Modulation not set");
break;
}
/*
* FIXME: send2GFSK will return as soon as the radio enter TX state.
* So the radio lock will be removed and then ssdv analysis can run.
* If ssdv analysis finishes before TX is done then tim.buf gets clobbered
*/
unlockRadio(); // Unlock radio
} else {
TRACE_ERROR("RAD > It is nonsense to transmit 0 bits, %d.%03d MHz, Pwr dBm, %s, %d bits",
msg->freq/1000000, (msg->freq%1000000)/1000, msg->power, getModulation(msg->mod), msg->bin_len
);
}
} else { // Frequency out of radio band
@ -370,9 +411,6 @@ bool transmitOnRadio(radioMSG_t *msg, bool shutdown) {
}
// Unlock radio
chMtxUnlock(&radio_mtx);
return true;
}
@ -393,3 +431,17 @@ uint32_t getFrequency(freq_conf_t *config)
}
}
void lockRadio(void)
{
chMtxLock(&radio_mtx);
// Wait for old feeder thread to terminate
if(feeder_thd != NULL) // No waiting on first use
chThdWait(feeder_thd);
}
void unlockRadio(void)
{
chMtxUnlock(&radio_mtx);
}

Wyświetl plik

@ -5,7 +5,7 @@
#include "hal.h"
#include "config.h"
#include "si4464.h"
#include "modules.h"
#include "threads.h"
// APRS region frequencies
#define APRS_FREQ_OTHER 144800000
@ -22,7 +22,10 @@
extern mutex_t radio_mtx;
bool transmitOnRadio(radioMSG_t *msg, bool shutdown);
void shutdownRadio(void);
uint32_t getFrequency(freq_conf_t *config);
void lockRadio(void);
void unlockRadio(void);
THD_FUNCTION(moduleRADIO, arg);

Wyświetl plik

@ -17,14 +17,14 @@ bool p_sleep(const sleep_conf_t *config)
case SLEEP_WHEN_VBAT_BELOW_THRES:
return getBatteryVoltageMV() < config->vbat_thres;
case SLEEP_WHEN_ISOL_BELOW_THRES:
return pac1720_getIsol() < config->isol_thres;
case SLEEP_WHEN_RBAT_BELOW_THRES:
return 0 < config->rbat_thres; // FIXME
case SLEEP_WHEN_VBAT_ABOVE_THRES:
return getBatteryVoltageMV() > config->vbat_thres;
case SLEEP_WHEN_ISOL_ABOVE_THRES:
return pac1720_getIsol() > config->isol_thres;
case SLEEP_WHEN_RBAT_ABOVE_THRES:
return 0 > config->rbat_thres; // FIXME
case SLEEP_WHEN_DISCHARGING:
case SLEEP_WHEN_CHARGING:
@ -48,7 +48,7 @@ systime_t waitForTrigger(systime_t prev, trigger_conf_t *config)
case TRIG_TIMEOUT: // Wait for specified timeout
return chThdSleepUntilWindowed(prev, prev + S2ST(config->timeout));
case TRIG_CONTINOUSLY: // Immediate trigger
case TRIG_CONTINUOUSLY: // Immediate trigger
return chVTGetSystemTimeX();
case TRIG_ONCE: // No trigger defined

Wyświetl plik

@ -29,7 +29,7 @@
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "threads.h"
#include "base.h"
#include "aprs.h"

Wyświetl plik

@ -0,0 +1,546 @@
#include "ch.h"
#include "hal.h"
#include "debug.h"
#include "threads.h"
#include "ov5640.h"
#include "pi2c.h"
#include "ssdv.h"
#include "aprs.h"
#include "radio.h"
#include "base.h"
#include <string.h>
#include "types.h"
#include "sleep.h"
#include "watchdog.h"
#include "flash.h"
const uint8_t noCameraFound[] = {
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48,
0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10,
0x0E, 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23,
0x25, 0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40,
0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D,
0x71, 0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x11, 0x12,
0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A, 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xFF, 0xC0,
0x00, 0x11, 0x08, 0x00, 0x80, 0x00, 0xA0, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11,
0x01, 0xFF, 0xC4, 0x00, 0x1B, 0x00, 0x00, 0x02, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x04, 0x05, 0x07, 0x06, 0x01, 0xFF, 0xC4,
0x00, 0x47, 0x10, 0x00, 0x00, 0x05, 0x02, 0x02, 0x05, 0x05, 0x0C, 0x06, 0x09, 0x05, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11, 0x12, 0x21, 0x06, 0x13, 0x31, 0x41,
0x81, 0x14, 0x15, 0x16, 0x42, 0x51, 0x22, 0x32, 0x53, 0x54, 0x61, 0x71, 0x91, 0x92, 0xA1, 0xC1,
0xE1, 0xE2, 0x36, 0x55, 0x63, 0x93, 0xA3, 0xB3, 0x23, 0x24, 0x52, 0x62, 0x64, 0x65, 0xA2, 0xB1,
0xD1, 0x07, 0x35, 0x43, 0x74, 0x82, 0xB2, 0xFF, 0xC4, 0x00, 0x1A, 0x01, 0x01, 0x01, 0x00, 0x03,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x03,
0x04, 0x06, 0x01, 0xFF, 0xC4, 0x00, 0x39, 0x11, 0x01, 0x00, 0x00, 0x03, 0x04, 0x06, 0x07, 0x05,
0x08, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x11, 0x05, 0x12, 0x51, 0xC1,
0x03, 0x04, 0x13, 0x31, 0x42, 0x81, 0x14, 0x15, 0x21, 0x43, 0x52, 0xA1, 0xE1, 0x06, 0x32, 0x33,
0x61, 0x62, 0x16, 0x22, 0x41, 0x63, 0x71, 0x82, 0xA2, 0xE2, 0x23, 0x24, 0xD1, 0x91, 0xFF, 0xDA,
0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0xF4, 0x00, 0x00, 0x0A,
0x7F, 0xAB, 0xC4, 0x49, 0xB4, 0xF8, 0x39, 0xE4, 0xD9, 0x21, 0x22, 0x43, 0x60, 0x01, 0x5E, 0x66,
0xC4, 0x71, 0xF7, 0x0B, 0xD6, 0x2F, 0x79, 0xCB, 0x34, 0x5B, 0x5F, 0x83, 0x9E, 0x4A, 0xC2, 0xF2,
0x20, 0x00, 0x97, 0xFA, 0xBC, 0x44, 0x9B, 0x4F, 0x74, 0x9C, 0xF2, 0x67, 0x21, 0x22, 0x43, 0x60,
0x00, 0x89, 0x7D, 0xEA, 0x3C, 0xE6, 0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xDE, 0x9B, 0xD9, 0xFE, 0xF7,
0xF6, 0xE6, 0xAA, 0x2F, 0x3D, 0x30, 0x00, 0xA7, 0xFA, 0xBC, 0x44, 0x9B, 0x4F, 0x83, 0x9E, 0x4C,
0xE4, 0x24, 0x48, 0x6C, 0x00, 0x2B, 0xCC, 0xD8, 0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66,
0x8B, 0x6B, 0xF0, 0x73, 0xC9, 0x58, 0x5E, 0x44, 0x00, 0x3A, 0xA8, 0xF2, 0x4E, 0x80, 0x01, 0x4F,
0xF5, 0x78, 0x89, 0x36, 0x9F, 0x07, 0x3C, 0x9B, 0x24, 0x24, 0x48, 0x6C, 0x00, 0x2B, 0xCC, 0xD8,
0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66, 0x8B, 0x6B, 0xF0, 0x73, 0xC9, 0x58, 0x5E, 0x44,
0x00, 0x12, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xEE, 0x93, 0x9E, 0x4C, 0xE4, 0x24, 0x48, 0x6C,
0x00, 0x11, 0x2F, 0xBD, 0x47, 0x9C, 0xC5, 0xEB, 0x17, 0xBC, 0xE5, 0x9B, 0xD3, 0x7B, 0x3F, 0xDE,
0xFE, 0xDC, 0xD5, 0x45, 0xE7, 0xA6, 0x00, 0x14, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xF0, 0x73,
0xC9, 0x9C, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79, 0x9B, 0x11, 0xC7, 0xDC, 0x2F, 0x58, 0xBD, 0xE7,
0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B, 0xC8, 0x80, 0x07, 0x55, 0x1E, 0x49, 0xD0, 0x00,
0x29, 0xFE, 0xAF, 0x11, 0x26, 0xD3, 0xE0, 0xE7, 0x93, 0x64, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79,
0x9B, 0x11, 0xC7, 0xDC, 0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B,
0xC8, 0x80, 0x02, 0x5F, 0xEA, 0xF1, 0x12, 0x6D, 0x3D, 0xD2, 0x73, 0xC9, 0x9C, 0x84, 0x89, 0x0D,
0x80, 0x02, 0x25, 0xF7, 0xA8, 0xF3, 0x98, 0xBD, 0x62, 0xF7, 0x9C, 0xB3, 0x7A, 0x6F, 0x67, 0xFB,
0xDF, 0xDB, 0x9A, 0xA8, 0xBC, 0xF4, 0xC0, 0x02, 0x9F, 0xEA, 0xF1, 0x12, 0x6D, 0x3E, 0x0E, 0x79,
0x33, 0x90, 0x91, 0x21, 0xB0, 0x00, 0xAF, 0x33, 0x62, 0x38, 0xFB, 0x85, 0xEB, 0x17, 0xBC, 0xE5,
0x9A, 0x2D, 0xAF, 0xC1, 0xCF, 0x25, 0x61, 0x79, 0x10, 0x00, 0xEA, 0xA3, 0xC9, 0x3A, 0x00, 0x05,
0x3F, 0xD5, 0xE2, 0x24, 0xDA, 0x7C, 0x1C, 0xF2, 0x6C, 0x90, 0x91, 0x21, 0xB0, 0x00, 0xAF, 0x33,
0x62, 0x38, 0xFB, 0x85, 0xEB, 0x17, 0xBC, 0xE5, 0x9A, 0x2D, 0xAF, 0xC1, 0xCF, 0x25, 0x61, 0x79,
0x10, 0x00, 0x4B, 0xFD, 0x5E, 0x22, 0x4D, 0xA7, 0xBA, 0x4E, 0x79, 0x33, 0x90, 0x91, 0x21, 0xB0,
0x00, 0x44, 0xBE, 0xF5, 0x1E, 0x73, 0x17, 0xAC, 0x5E, 0xF3, 0x96, 0x6F, 0x4D, 0xEC, 0xFF, 0x00,
0x7B, 0xFB, 0x73, 0x55, 0x17, 0x9E, 0x98, 0x00, 0x53, 0xFD, 0x5E, 0x22, 0x4D, 0xA7, 0xC1, 0xCF,
0x26, 0x72, 0x12, 0x24, 0x36, 0x00, 0x15, 0xE6, 0x6C, 0x47, 0x1F, 0x70, 0xBD, 0x62, 0xF7, 0x9C,
0xB3, 0x45, 0xB5, 0xF8, 0x39, 0xE4, 0xAC, 0x2F, 0x22, 0x00, 0x1D, 0x54, 0x79, 0x27, 0x43, 0x0A,
0xAF, 0xA4, 0xCC, 0xD2, 0xA6, 0x9C, 0x67, 0x23, 0xB8, 0xE2, 0x89, 0x24, 0xAC, 0x49, 0x32, 0x22,
0xCC, 0x06, 0x7B, 0x9A, 0x6B, 0x1D, 0x76, 0xB4, 0x47, 0x4A, 0xDF, 0xBC, 0x43, 0x8F, 0x5B, 0xD5,
0xA3, 0xA7, 0xBB, 0x48, 0xD2, 0x95, 0x65, 0x2C, 0xD4, 0x43, 0xA6, 0x31, 0xFC, 0x51, 0xDF, 0x58,
0x87, 0x17, 0x56, 0x4F, 0xE2, 0x65, 0x7C, 0x74, 0xC6, 0x3F, 0x8A, 0x3B, 0xEB, 0x10, 0x75, 0x64,
0xFE, 0x22, 0xF9, 0x4F, 0xE9, 0x6B, 0x0E, 0x92, 0x6D, 0x15, 0xC2, 0xB7, 0xEF, 0x10, 0xA3, 0xA8,
0x68, 0x63, 0xAA, 0xDE, 0xAC, 0x6B, 0x5A, 0x79, 0x55, 0xC3, 0xAE, 0xEA, 0xF1, 0xD6, 0x2E, 0xD2,
0x34, 0xA5, 0x72, 0x2B, 0xA4, 0xEC, 0xF8, 0xB3, 0x9E, 0xB1, 0x0A, 0x1B, 0x68, 0x60, 0xE0, 0xEA,
0xC9, 0xFC, 0x43, 0xA4, 0xEC, 0xF8, 0xB3, 0x9E, 0xB1, 0x06, 0xDA, 0x18, 0x1D, 0x59, 0x3F, 0x89,
0x07, 0x34, 0x91, 0x95, 0xDA, 0xD1, 0xD6, 0x56, 0xF2, 0x90, 0xE3, 0xD6, 0xE4, 0xDB, 0xDD, 0xA7,
0x65, 0x2A, 0xCA, 0x5B, 0x3A, 0x68, 0x71, 0x21, 0xD2, 0x16, 0xBC, 0x02, 0xFD, 0x24, 0x38, 0xBA,
0x1C, 0xD8, 0xB2, 0xE8, 0x13, 0x62, 0x3A, 0x42, 0xD7, 0x80, 0x5F, 0xA4, 0x83, 0xA1, 0xCD, 0x89,
0xD0, 0x26, 0xC4, 0xB7, 0xAB, 0xAD, 0x3A, 0x49, 0x22, 0x65, 0x65, 0x6F, 0x29, 0x0A, 0x3A, 0x87,
0xFA, 0xB7, 0xAB, 0xDB, 0x5A, 0x79, 0x55, 0x56, 0xCD, 0xFF, 0x00, 0x4E, 0xFD, 0xEE, 0xDA, 0xD3,
0xCA, 0xBF, 0xF4, 0x9E, 0x77, 0x6F, 0xC1, 0x2B, 0xD2, 0x28, 0xF4, 0xC9, 0x70, 0x55, 0xE9, 0xF2,
0xE0, 0x39, 0xDD, 0xBF, 0x04, 0xAF, 0x48, 0x74, 0xC9, 0x70, 0x3A, 0x7C, 0xB8, 0x20, 0xE5, 0x51,
0x0B, 0xB5, 0x9B, 0x51, 0x5B, 0xCA, 0x38, 0xF5, 0xB9, 0xF6, 0xF7, 0x69, 0xD9, 0x4A, 0xB2, 0x96,
0xD1, 0x96, 0x1C, 0x28, 0x73, 0x8A, 0x3C, 0x1A, 0xBD, 0x23, 0x8B, 0x63, 0x1C, 0x59, 0x75, 0x9C,
0x9E, 0x11, 0xCE, 0x28, 0xF0, 0x6A, 0xF4, 0x86, 0xC6, 0x38, 0x9D, 0x67, 0x27, 0x84, 0xA7, 0xE6,
0x25, 0xD2, 0x4D, 0x90, 0x65, 0x61, 0x47, 0x50, 0xD3, 0x43, 0x55, 0xBD, 0x58, 0x56, 0xB4, 0xF2,
0xAB, 0x87, 0x5D, 0xD6, 0x21, 0xAC, 0x5D, 0xA4, 0x29, 0x4A, 0xE4, 0x5A, 0x5D, 0x25, 0x28, 0x8A,
0xDB, 0x45, 0x4D, 0x16, 0xBF, 0x2E, 0x92, 0x78, 0x49, 0x08, 0x6F, 0x4F, 0x8C, 0x94, 0x30, 0x50,
0x60, 0xEA, 0xA3, 0xC9, 0x3A, 0x18, 0x6C, 0x9B, 0x85, 0xA5, 0x55, 0x63, 0x65, 0xC4, 0xB4, 0xE9,
0x52, 0x97, 0x81, 0xC5, 0x2B, 0x09, 0x20, 0xEE, 0x9B, 0x19, 0x99, 0xEC, 0xB7, 0x68, 0x08, 0xD3,
0xA4, 0xB4, 0xFE, 0x93, 0xE8, 0xFA, 0x1C, 0x92, 0xC4, 0xBA, 0x8B, 0x68, 0x7C, 0xA5, 0x3E, 0xC9,
0x92, 0x89, 0x57, 0x4A, 0xB0, 0x16, 0x22, 0xC8, 0xCC, 0x8A, 0xE0, 0x30, 0xE5, 0x4B, 0x4D, 0x5A,
0x4C, 0x08, 0x4E, 0xD7, 0x64, 0xCE, 0x69, 0xD9, 0x48, 0x4A, 0xD0, 0xEB, 0x1A, 0xB2, 0x41, 0x19,
0xDB, 0x15, 0xEE, 0x79, 0xE6, 0x60, 0x17, 0xA4, 0x95, 0x99, 0xBC, 0xB6, 0xA1, 0x4A, 0x49, 0xA5,
0xA8, 0x08, 0x70, 0xDA, 0x44, 0x74, 0xA0, 0x89, 0x29, 0x24, 0x2B, 0x23, 0x2C, 0xB6, 0xE5, 0x7E,
0x20, 0x3D, 0x0D, 0x6E, 0x94, 0xFA, 0x34, 0x35, 0x50, 0x8D, 0x85, 0xA5, 0x10, 0x18, 0x61, 0xF4,
0x9D, 0xB6, 0xB8, 0x66, 0xAD, 0x6E, 0x7D, 0x84, 0x4A, 0xB8, 0x06, 0x9B, 0x69, 0x98, 0x5A, 0x38,
0xDA, 0x52, 0x5A, 0xF8, 0x4C, 0x44, 0x90, 0x5B, 0x6E, 0xA6, 0xD6, 0x78, 0x17, 0x97, 0x90, 0xC9,
0x07, 0xC4, 0xC0, 0x78, 0x5A, 0xE7, 0xFB, 0xED, 0x43, 0xFE, 0xCB, 0x9F, 0xFD, 0x18, 0x0A, 0x20,
0x3D, 0x66, 0x88, 0x2A, 0x42, 0x28, 0x55, 0xD5, 0x43, 0x92, 0xDC, 0x57, 0xCB, 0x93, 0xE1, 0x75,
0xC7, 0x09, 0x09, 0x4F, 0x74, 0xAB, 0xDC, 0xCF, 0x22, 0xCA, 0xE5, 0xC4, 0x05, 0x6D, 0x23, 0xA9,
0x13, 0x55, 0x98, 0x72, 0xA9, 0xF2, 0x9B, 0x54, 0xC6, 0xA2, 0xA1, 0x2F, 0xC8, 0x8E, 0x45, 0x65,
0x3B, 0x63, 0x25, 0x1D, 0xED, 0x63, 0xB9, 0x1E, 0xD0, 0x1A, 0xF5, 0xA2, 0x3A, 0xA7, 0xFA, 0x90,
0xCD, 0x3A, 0x6B, 0xA6, 0xA8, 0x8D, 0x38, 0x93, 0x43, 0x6A, 0xD8, 0x57, 0x6D, 0x2A, 0x32, 0xE2,
0x65, 0x6E, 0x20, 0x31, 0xF9, 0xDA, 0x65, 0x66, 0xB5, 0x1A, 0x9F, 0x38, 0xCB, 0x91, 0xAE, 0x6A,
0x0B, 0x93, 0xE1, 0x24, 0x93, 0x65, 0x8B, 0x0E, 0x12, 0xB6, 0x65, 0x91, 0x99, 0x00, 0xD3, 0xA9,
0xC8, 0x72, 0xA1, 0x07, 0x49, 0x99, 0x95, 0x85, 0x6D, 0xD3, 0xDF, 0x41, 0x45, 0x2C, 0x04, 0x5A,
0x92, 0xD6, 0x1A, 0x6C, 0x56, 0x2D, 0x96, 0x22, 0x20, 0x1E, 0x28, 0x00, 0x03, 0xDA, 0x68, 0xCB,
0x8E, 0x15, 0x1A, 0x22, 0x29, 0x8F, 0xC7, 0x69, 0xF3, 0x9B, 0xFA, 0xE9, 0x38, 0xB4, 0xA5, 0x4A,
0x6F, 0x2B, 0x77, 0xDB, 0x53, 0x6D, 0xC5, 0xBC, 0x05, 0xEA, 0x04, 0x15, 0x40, 0xAF, 0xD7, 0x25,
0xC2, 0x8E, 0x6B, 0x26, 0x1F, 0x4B, 0x0D, 0xB6, 0x82, 0xCB, 0x0A, 0x9C, 0x23, 0x59, 0x17, 0x99,
0x24, 0x03, 0xC8, 0xD7, 0xA1, 0x73, 0x7E, 0x91, 0x4C, 0x8A, 0x45, 0x64, 0xA1, 0xD3, 0x34, 0x97,
0x62, 0x4F, 0x32, 0xF6, 0x19, 0x0E, 0x8D, 0x53, 0xE3, 0x4A, 0xF9, 0x36, 0xE5, 0x61, 0xE9, 0x5A,
0x1D, 0x54, 0x79, 0x27, 0x43, 0xC4, 0xE9, 0x1D, 0x49, 0xEA, 0x75, 0x7A, 0x66, 0xA5, 0x2D, 0xAB,
0x94, 0xC3, 0xE4, 0xEB, 0xC6, 0x46, 0x76, 0x4A, 0xB6, 0x99, 0x58, 0xF6, 0xE4, 0x03, 0x06, 0x93,
0x52, 0x7A, 0x91, 0x52, 0x6A, 0x74, 0x74, 0xB6, 0xA7, 0x5A, 0xBE, 0x12, 0x70, 0x8C, 0xD3, 0x99,
0x19, 0x67, 0x63, 0x2E, 0xD0, 0x0C, 0x9B, 0x53, 0x4C, 0xB6, 0x92, 0x86, 0xE9, 0xD0, 0xA2, 0xA9,
0x2A, 0x25, 0x6B, 0x23, 0xA5, 0x49, 0x57, 0x9A, 0xE6, 0xA3, 0xCB, 0xFC, 0x00, 0xB3, 0x23, 0x49,
0x1F, 0x94, 0x93, 0x39, 0x30, 0xA0, 0xBA, 0xF2, 0xB0, 0x6B, 0x1F, 0x36, 0x8C, 0x9C, 0x70, 0x92,
0x64, 0x76, 0x33, 0x23, 0xDF, 0x62, 0x23, 0xB1, 0x16, 0x40, 0x23, 0xD2, 0x29, 0x87, 0x53, 0x9B,
0x3D, 0x6D, 0xB2, 0xB7, 0x26, 0xB4, 0xA6, 0x5D, 0x42, 0x92, 0x78, 0x70, 0x99, 0x11, 0x58, 0xB3,
0xBE, 0xE2, 0xDE, 0x02, 0x71, 0xF4, 0x9E, 0x7C, 0x7A, 0x84, 0x39, 0x8D, 0xA5, 0x92, 0x72, 0x24,
0x62, 0x8A, 0x94, 0xD9, 0x58, 0x56, 0x82, 0x23, 0xB6, 0x2C, 0xF3, 0x3C, 0xEF, 0xBB, 0x61, 0x00,
0xCA, 0x97, 0x21, 0x72, 0xE5, 0xBD, 0x25, 0xC2, 0x49, 0x2D, 0xE7, 0x14, 0xE2, 0x89, 0x3B, 0x08,
0xCC, 0xEF, 0x90, 0x05, 0x00, 0xB9, 0x1A, 0xA4, 0xF4, 0x6A, 0x6C, 0xD8, 0x28, 0x4B, 0x66, 0xD4,
0xCC, 0x1A, 0xC3, 0x51, 0x1E, 0x22, 0xC0, 0x77, 0x2B, 0x66, 0x01, 0x31, 0x1F, 0x28, 0xD2, 0x50,
0xF2, 0x98, 0x69, 0xF2, 0x4D, 0xFF, 0x00, 0x46, 0xF1, 0x19, 0xA5, 0x59, 0x5B, 0x3B, 0x19, 0x18,
0x0B, 0xF5, 0x5A, 0xFC, 0x8A, 0x9C, 0xF6, 0xA7, 0x29, 0x88, 0xF1, 0xE5, 0x36, 0xA2, 0x5E, 0xB5,
0x84, 0x99, 0x1A, 0x8C, 0xAD, 0x63, 0x3B, 0x99, 0xEC, 0xC2, 0x56, 0x00, 0x4E, 0xAF, 0x3F, 0x34,
0x89, 0x47, 0x16, 0x23, 0x12, 0x35, 0x84, 0xEA, 0xA4, 0x30, 0xDE, 0x07, 0x14, 0xA2, 0xDE, 0x67,
0x7E, 0xD3, 0xBE, 0x56, 0xCC, 0x04, 0xAA, 0x1A, 0x45, 0x2E, 0x7C, 0x57, 0x58, 0x53, 0x31, 0x98,
0x27, 0xD4, 0x4B, 0x7D, 0x6C, 0x37, 0x85, 0x4F, 0x99, 0x6C, 0x35, 0x67, 0x9E, 0x79, 0xEE, 0xCC,
0x06, 0x40, 0x00, 0x06, 0x95, 0x32, 0xB2, 0xBA, 0x6B, 0x64, 0x4D, 0x43, 0x86, 0xEB, 0x89, 0x5E,
0xB1, 0x0F, 0x3A, 0xD6, 0x25, 0xB6, 0x76, 0x2D, 0x87, 0x7D, 0xD6, 0xB9, 0x5C, 0x8F, 0x30, 0x10,
0x7A, 0xB1, 0x25, 0xFA, 0x62, 0xA0, 0xB9, 0x80, 0xD0, 0xB9, 0x27, 0x25, 0x6E, 0x67, 0x8D, 0x6B,
0x32, 0xB6, 0x67, 0x7B, 0x7B, 0x00, 0x15, 0x1A, 0x9B, 0xD5, 0x69, 0x8D, 0xC8, 0x90, 0x86, 0xD2,
0xEA, 0x5B, 0x4B, 0x66, 0x68, 0x23, 0x2C, 0x76, 0xDE, 0x77, 0x33, 0xCC, 0x74, 0x6A, 0x9F, 0x1A,
0x57, 0xC9, 0xB7, 0x16, 0x3D, 0x2B, 0x43, 0xAA, 0x8F, 0x24, 0xE8, 0x79, 0xEE, 0x4D, 0x06, 0x4E,
0x97, 0x4F, 0x3A, 0x93, 0x24, 0xEC, 0x76, 0x69, 0xE6, 0xE9, 0x91, 0xEE, 0xB1, 0xA7, 0x32, 0xF2,
0xDA, 0xE0, 0x15, 0x0F, 0x46, 0x22, 0x25, 0xB8, 0xCC, 0x4C, 0x67, 0x12, 0x9A, 0x96, 0xF9, 0x3A,
0xB4, 0x9D, 0x8D, 0xC4, 0x21, 0x37, 0x49, 0x5F, 0xB0, 0xF2, 0xE0, 0x60, 0x30, 0xEA, 0x4C, 0xC5,
0x99, 0xA3, 0x6D, 0x55, 0x98, 0x88, 0xDC, 0x47, 0x53, 0x2C, 0xE3, 0x2D, 0x0D, 0x19, 0xE1, 0x51,
0x61, 0xC4, 0x47, 0x63, 0x33, 0xB1, 0xEE, 0x01, 0x82, 0x03, 0xD2, 0x56, 0xCA, 0x05, 0x11, 0xE5,
0xD2, 0x13, 0x4E, 0x65, 0xF7, 0x50, 0xC1, 0x13, 0xD2, 0x16, 0xA5, 0x63, 0xD6, 0xA9, 0x37, 0xBA,
0x73, 0xB1, 0x11, 0x5C, 0xB2, 0xB6, 0xE0, 0x16, 0xE1, 0x51, 0xA2, 0x39, 0xA2, 0x26, 0x4A, 0x65,
0x27, 0x3D, 0xF8, 0xEF, 0x4C, 0x43, 0xA7, 0xDF, 0x21, 0x2D, 0xA9, 0x25, 0x84, 0x8B, 0xCA, 0x57,
0x01, 0xA7, 0x40, 0xD1, 0xEA, 0x74, 0xEA, 0x35, 0x16, 0x4B, 0xB1, 0x9B, 0x52, 0xCD, 0xC5, 0x9C,
0x83, 0x33, 0xB6, 0x34, 0xF7, 0x64, 0x57, 0xED, 0xEE, 0xB0, 0x17, 0x10, 0x15, 0x91, 0x4E, 0x88,
0xCC, 0x0A, 0x93, 0xAD, 0x44, 0xA6, 0x1B, 0x8D, 0xD5, 0x9D, 0x61, 0x27, 0x39, 0x78, 0x10, 0x96,
0xC8, 0xAE, 0x49, 0x23, 0xB9, 0x67, 0xF1, 0x00, 0x8A, 0x5C, 0x02, 0x90, 0x8A, 0xE3, 0xAD, 0xD3,
0x29, 0xF3, 0x25, 0xB1, 0xC9, 0xF5, 0x2C, 0xB0, 0x66, 0xB6, 0x73, 0xBE, 0x2C, 0x36, 0x57, 0x66,
0x67, 0x9E, 0xD2, 0x01, 0x17, 0x68, 0xB0, 0xDD, 0xD3, 0x1A, 0x64, 0x24, 0xB2, 0xDB, 0x04, 0xB6,
0x10, 0xF4, 0xC6, 0x09, 0x46, 0xA4, 0xB6, 0xB2, 0x23, 0x52, 0x93, 0xB4, 0xEC, 0x56, 0x22, 0x2D,
0xBB, 0xC0, 0x64, 0xE9, 0x4C, 0x56, 0x19, 0x9B, 0x1A, 0x4C, 0x46, 0x92, 0xCC, 0x79, 0xB1, 0x9B,
0x7D, 0x2D, 0xA7, 0x63, 0x66, 0x65, 0x63, 0x4D, 0xF7, 0xE6, 0x5E, 0xD0, 0x18, 0xA0, 0x3D, 0xAE,
0x95, 0x45, 0x8F, 0x4E, 0x7A, 0x6B, 0x51, 0xA1, 0x51, 0x52, 0xCA, 0x52, 0x44, 0x92, 0x37, 0x3F,
0x58, 0x2C, 0x49, 0x2C, 0xC9, 0x38, 0xB6, 0xDC, 0xEE, 0x59, 0x6C, 0x00, 0x4B, 0xA5, 0x53, 0x8A,
0x75, 0x4A, 0x88, 0x88, 0x48, 0x4A, 0xA1, 0x42, 0xD7, 0x22, 0x51, 0x29, 0x5A, 0xC5, 0x38, 0x49,
0x4A, 0x8E, 0xF9, 0xDA, 0xC7, 0x7B, 0x5A, 0xC0, 0x28, 0xBD, 0x4F, 0x88, 0x9A, 0x8E, 0x8C, 0x36,
0x4C, 0x20, 0x91, 0x29, 0xB6, 0x4D, 0xE2, 0xFD, 0xB3, 0x35, 0xD8, 0xEF, 0xC0, 0x06, 0x8D, 0x56,
0x89, 0x01, 0x54, 0x29, 0xBC, 0x9E, 0x3A, 0x1B, 0x96, 0xDC, 0x99, 0x0B, 0x6D, 0x49, 0xCA, 0xE8,
0x6D, 0xC3, 0x23, 0x4F, 0x04, 0x9D, 0xFF, 0x00, 0xF2, 0x01, 0x95, 0xB8, 0x74, 0xEA, 0x52, 0x6A,
0xEF, 0x35, 0x4C, 0x8A, 0xEE, 0xA6, 0x4B, 0x2D, 0xA1, 0x0E, 0x11, 0xD9, 0x29, 0x53, 0x69, 0x33,
0xB5, 0x8C, 0xB7, 0x80, 0xF3, 0x9A, 0x41, 0x02, 0x3C, 0x2A, 0x84, 0x65, 0xC3, 0x4A, 0x91, 0x1E,
0x5C, 0x66, 0xE4, 0xA1, 0xB5, 0x2A, 0xE6, 0x82, 0x51, 0x1F, 0x73, 0x7D, 0xFB, 0x07, 0x46, 0xA9,
0xF1, 0xA5, 0x7C, 0x9B, 0x72, 0x88, 0xF4, 0xAD, 0x0E, 0xAA, 0x3C, 0x93, 0xA1, 0xE4, 0xEA, 0xF2,
0xD7, 0x06, 0xB9, 0x51, 0x57, 0x26, 0x79, 0xE4, 0xCA, 0xA7, 0xAA, 0x32, 0x4D, 0x09, 0xC8, 0x8D,
0x56, 0xCF, 0xD8, 0x01, 0x68, 0xD2, 0x89, 0x4D, 0xC4, 0xA5, 0xA4, 0xE9, 0xCE, 0xAD, 0xE8, 0x86,
0x64, 0xE9, 0xA8, 0x8C, 0x89, 0xE4, 0xE1, 0xC1, 0xD9, 0x7B, 0xE1, 0xB6, 0x7E, 0x41, 0xF2, 0x33,
0x42, 0x1B, 0xE2, 0x32, 0xEA, 0x52, 0xC9, 0xCA, 0x63, 0x54, 0xDA, 0x74, 0x09, 0x2C, 0xC5, 0x43,
0xA6, 0xFA, 0x8D, 0xD3, 0xC4, 0xA5, 0xAC, 0xCA, 0xDB, 0x88, 0xAC, 0x44, 0x59, 0x0C, 0x76, 0x92,
0x62, 0x51, 0x91, 0xC9, 0x64, 0x78, 0x07, 0x7D, 0x43, 0x0D, 0xA4, 0x98, 0x94, 0x6E, 0x4D, 0x9F,
0x1E, 0xA2, 0xC9, 0x3D, 0x3E, 0x97, 0x28, 0xEA, 0x09, 0x63, 0x55, 0xAC, 0x42, 0xEC, 0x87, 0x0C,
0x8A, 0xC9, 0x5A, 0x8A, 0xD7, 0xB9, 0x65, 0xB0, 0xF3, 0xB0, 0xCA, 0x58, 0xC2, 0x6D, 0xDD, 0xAF,
0x91, 0x9A, 0x12, 0xEF, 0x68, 0x33, 0xA5, 0x92, 0xA3, 0xD4, 0x21, 0x6A, 0x63, 0xC9, 0xE6, 0xD8,
0xEC, 0x25, 0x95, 0x46, 0x3F, 0xF9, 0x2C, 0x93, 0x2B, 0x9E, 0x5B, 0x73, 0x2F, 0x40, 0xCA, 0x91,
0x63, 0xB4, 0x93, 0x15, 0x04, 0xD5, 0xD4, 0xDA, 0x28, 0xAD, 0xB5, 0x1A, 0x49, 0x22, 0x9C, 0xEA,
0x96, 0xBF, 0xB4, 0x49, 0xB8, 0x4B, 0x22, 0xF6, 0x05, 0x22, 0x6D, 0x24, 0xC4, 0xE7, 0x2A, 0xD0,
0xE5, 0xC2, 0x99, 0x16, 0x74, 0x19, 0xD8, 0x1F, 0xA8, 0x2E, 0x6A, 0x4D, 0x93, 0x24, 0x99, 0x5C,
0xAC, 0x44, 0x77, 0x23, 0xED, 0x31, 0xF2, 0x31, 0xA6, 0xF2, 0xFC, 0xB8, 0xA9, 0xA2, 0x6B, 0x31,
0xA9, 0xD5, 0x68, 0x31, 0x22, 0xCA, 0xD5, 0x4D, 0xD4, 0xEA, 0xCD, 0xD3, 0x23, 0x52, 0x30, 0x1D,
0xCE, 0xF6, 0x22, 0xBD, 0xFC, 0x83, 0x1B, 0xD2, 0xE2, 0xFB, 0x7E, 0x5C, 0x5A, 0x48, 0xD2, 0x6C,
0x3F, 0xAE, 0x14, 0x39, 0x25, 0x53, 0x38, 0x27, 0x11, 0x4F, 0x15, 0xAC, 0xA3, 0xCA, 0xCE, 0x1E,
0x57, 0xC5, 0x90, 0x5E, 0x97, 0x12, 0xFC, 0xB8, 0xB3, 0x6A, 0xF5, 0x77, 0xEA, 0xD4, 0xA8, 0x2C,
0xCB, 0x6D, 0xF5, 0xCB, 0x8C, 0xA5, 0xDD, 0xE5, 0x16, 0x4B, 0x4A, 0x8E, 0xFE, 0x92, 0xB1, 0x10,
0xCA, 0x5F, 0xBD, 0xBB, 0xB5, 0x94, 0xBF, 0x7B, 0xDD, 0xED, 0x62, 0xEA, 0x9C, 0xFD, 0x85, 0x7A,
0x06, 0x57, 0x66, 0xC1, 0x95, 0xC9, 0xB0, 0x69, 0x69, 0x14, 0xE3, 0xAC, 0x56, 0xE4, 0x4F, 0x6D,
0x87, 0x1B, 0x4B, 0xB8, 0x6C, 0x95, 0x15, 0xCC, 0xAC, 0x92, 0x2F, 0x70, 0x5D, 0x9B, 0x02, 0xE4,
0xD8, 0x34, 0xE4, 0x69, 0x1B, 0x6E, 0x72, 0xA9, 0x69, 0x80, 0xEA, 0x6A, 0x72, 0xE3, 0x72, 0x67,
0x5C, 0x35, 0xFE, 0x8E, 0xD6, 0x22, 0x35, 0x12, 0x6D, 0x7B, 0x99, 0x11, 0x6F, 0xCB, 0xFB, 0xFC,
0x8C, 0x29, 0xBD, 0xF6, 0xE4, 0xD8, 0x21, 0x12, 0xB9, 0x10, 0x9B, 0xA7, 0x39, 0x3A, 0x9F, 0x21,
0xE9, 0x54, 0xD2, 0xB3, 0x2A, 0x6D, 0xDC, 0x29, 0x59, 0x11, 0xDD, 0x24, 0xA2, 0xB1, 0xEC, 0xF2,
0x0C, 0x6B, 0x03, 0x67, 0x3E, 0x00, 0xB4, 0x95, 0xC3, 0x28, 0x2B, 0x54, 0x75, 0x1B, 0xCC, 0x4A,
0x75, 0xF7, 0x72, 0xEE, 0x5C, 0x27, 0x0F, 0xBA, 0x49, 0x17, 0x94, 0x8C, 0xCB, 0x88, 0x56, 0x06,
0xCE, 0x7C, 0x16, 0x6A, 0x1A, 0x49, 0x02, 0xA2, 0x75, 0x04, 0xCB, 0x85, 0x2C, 0x9A, 0x94, 0xF3,
0x6E, 0xA4, 0x9B, 0x5A, 0x52, 0x65, 0x85, 0x04, 0x9B, 0x19, 0x99, 0x1F, 0x60, 0xCA, 0x58, 0x46,
0x6D, 0xDD, 0xAC, 0x63, 0x2C, 0x65, 0xDF, 0x06, 0x25, 0x56, 0xA4, 0xBA, 0xAD, 0x41, 0x0F, 0x6A,
0x49, 0x96, 0x9B, 0x42, 0x5A, 0x69, 0xA2, 0x3B, 0x93, 0x68, 0x49, 0x64, 0x57, 0xDE, 0x3A, 0xB5,
0x59, 0x26, 0x86, 0x9A, 0x58, 0xC6, 0x0C, 0x26, 0x8F, 0x61, 0x03, 0xD1, 0x34, 0xBA, 0xA8, 0xF2,
0x4E, 0x80, 0x01, 0x4F, 0xF5, 0x78, 0x89, 0x36, 0x9F, 0x07, 0x3C, 0x9B, 0x24, 0x24, 0x48, 0x6C,
0x00, 0x2B, 0xCC, 0xD8, 0x8E, 0x3E, 0xE1, 0x7A, 0xC5, 0xEF, 0x39, 0x66, 0x8B, 0x6B, 0xF0, 0x73,
0xC9, 0x58, 0x5E, 0x44, 0x00, 0x12, 0xFF, 0x00, 0x57, 0x88, 0x93, 0x69, 0xEE, 0x93, 0x9E, 0x4C,
0xE4, 0x24, 0x48, 0x6C, 0x00, 0x11, 0x2F, 0xBD, 0x47, 0x9C, 0xC5, 0xEB, 0x17, 0xBC, 0xE5, 0x9B,
0xD3, 0x7B, 0x3F, 0xDE, 0xFE, 0xDC, 0xD5, 0x45, 0xE7, 0xA6, 0x00, 0x14, 0xFF, 0x00, 0x57, 0x88,
0x93, 0x69, 0xF0, 0x73, 0xC9, 0x9C, 0x84, 0x89, 0x0D, 0x80, 0x05, 0x79, 0x9B, 0x11, 0xC7, 0xDC,
0x2F, 0x58, 0xBD, 0xE7, 0x2C, 0xD1, 0x6D, 0x7E, 0x0E, 0x79, 0x2B, 0x0B, 0xC8, 0x80, 0x07, 0xAC,
0xE9, 0xA2, 0x7E, 0xAF, 0x3F, 0xBF, 0xF9, 0x44, 0x8E, 0xAC, 0x8F, 0x8F, 0xCB, 0xD5, 0xB2, 0xF8,
0xE9, 0xA2, 0x7E, 0xAF, 0x3F, 0xBF, 0xF9, 0x43, 0xAB, 0x23, 0xE3, 0xF2, 0xF5, 0x2F, 0x96, 0xEE,
0x98, 0x92, 0xAD, 0x6A, 0x7E, 0xCF, 0xB7, 0xF9, 0x44, 0x9B, 0x4E, 0xCC, 0xF7, 0x3E, 0xFE, 0x3F,
0x87, 0xE9, 0xF3, 0x6C, 0x92, 0x72, 0xFA, 0x5D, 0xFC, 0xBF, 0xF1, 0xFE, 0x51, 0x23, 0xAA, 0xFE,
0xBF, 0x2F, 0x56, 0xCB, 0xE3, 0xA5, 0xDF, 0xCB, 0xFF, 0x00, 0x1F, 0xE5, 0x0E, 0xAB, 0xFA, 0xFC,
0xBD, 0x4B, 0xE6, 0x35, 0xA4, 0x05, 0x34, 0x8E, 0xF1, 0x4D, 0xBC, 0x1F, 0x6B, 0x7B, 0xDF, 0x81,
0x76, 0x0A, 0xF6, 0x66, 0xA5, 0xB2, 0xBF, 0xF7, 0xAB, 0x5A, 0x7E, 0x1F, 0xAF, 0xCD, 0xAE, 0x7B,
0x3F, 0xA7, 0xF1, 0x5D, 0xBB, 0xF2, 0xAE, 0xFE, 0x70, 0xC0, 0xCE, 0x71, 0x4F, 0x82, 0x3F, 0x5B,
0xE0, 0x2B, 0x6C, 0x63, 0x8B, 0x5F, 0xD9, 0xBF, 0xCD, 0xFE, 0x3F, 0xD8, 0x73, 0x8A, 0x7C, 0x11,
0xFA, 0xDF, 0x00, 0xD8, 0xC7, 0x13, 0xEC, 0xDF, 0xE6, 0xFF, 0x00, 0x1F, 0xEC, 0xA9, 0x3A, 0xAE,
0x4D, 0x6A, 0xED, 0x1F, 0x15, 0xEF, 0xD7, 0xB7, 0x67, 0x90, 0x4D, 0xB4, 0x35, 0x5B, 0xF7, 0x7B,
0x71, 0xC9, 0xA3, 0x4F, 0x62, 0xF4, 0x7A, 0x7F, 0x92, 0xB5, 0xF9, 0x7A, 0xAA, 0x73, 0xEF, 0xF0,
0xBF, 0x89, 0xF0, 0x13, 0x3A, 0x17, 0xD5, 0xE5, 0xEA, 0xE7, 0xEA, 0xFF, 0x00, 0xAB, 0xCB, 0xD4,
0x73, 0xEF, 0xF0, 0xBF, 0x89, 0xF0, 0x0E, 0x85, 0xF5, 0x79, 0x7A, 0x9D, 0x5F, 0xF5, 0x79, 0x7A,
0xAD, 0xC1, 0x7C, 0xAA, 0xA4, 0xE1, 0x1A, 0x4D, 0x9D, 0x55, 0xB7, 0xE2, 0xBD, 0xEF, 0xE6, 0xEC,
0x14, 0xAC, 0xF9, 0x3A, 0x3D, 0xEE, 0xDA, 0xD6, 0x99, 0xBA, 0x34, 0x3A, 0xD7, 0x55, 0xD7, 0xB2,
0xF5, 0xEE, 0x54, 0xA7, 0xFE, 0xE2, 0xB7, 0xCD, 0xC9, 0xF0, 0xA7, 0xEA, 0xFC, 0x45, 0x3D, 0xB4,
0x70, 0x6F, 0xFB, 0x49, 0xF9, 0x5F, 0xCB, 0xFA, 0x8E, 0x6E, 0x4F, 0x85, 0x3F, 0x57, 0xE2, 0x1B,
0x68, 0xE0, 0x7D, 0xA4, 0xFC, 0xAF, 0xE5, 0xFD, 0x59, 0xB5, 0x84, 0x14, 0x2D, 0x4D, 0x8C, 0xDC,
0xC7, 0x8B, 0xC9, 0x6B, 0x5B, 0xFC, 0x8E, 0x1D, 0x77, 0xFC, 0xB7, 0x7F, 0x0A, 0x57, 0x27, 0x6E,
0xA9, 0x6E, 0x6D, 0xAF, 0x7F, 0x8E, 0x94, 0xA7, 0xE3, 0xFA, 0xFC, 0x99, 0xBC, 0xB3, 0xEC, 0xFF,
0x00, 0xAB, 0xE0, 0x27, 0xEC, 0x3E, 0x6E, 0xEE, 0xB4, 0xFA, 0x3C, 0xFD, 0x07, 0x2C, 0xFB, 0x3F,
0xEA, 0xF8, 0x06, 0xC3, 0xE6, 0x75, 0xA7, 0xD1, 0xE7, 0xE8, 0xD2, 0xA3, 0xD2, 0xCA, 0xBA, 0x4F,
0x5D, 0xE3, 0x8F, 0xA8, 0xC3, 0xD4, 0xC7, 0x8B, 0x15, 0xFC, 0xA5, 0x6E, 0xF7, 0xDA, 0x3B, 0xF5,
0x2D, 0x3F, 0x45, 0xBD, 0xD9, 0x5A, 0xD3, 0xE5, 0xBA, 0xBF, 0xAE, 0x2E, 0x2D, 0x6F, 0x58, 0xE9,
0x37, 0x7B, 0x29, 0x4A, 0xFC, 0xFF, 0x00, 0xE3, 0x4B, 0xA1, 0x69, 0xFA, 0xC0, 0xFE, 0xE3, 0xE6,
0x1D, 0xFD, 0x67, 0x1F, 0x07, 0x9F, 0xA3, 0x86, 0xE0, 0xE8, 0x5A, 0x7E, 0xB0, 0x3F, 0xB8, 0xF9,
0x83, 0xAC, 0xE3, 0xE0, 0xF3, 0xF4, 0x2E, 0x3C, 0xA5, 0x8C, 0x51, 0xE9, 0x1A, 0x2F, 0x13, 0x58,
0xB1, 0x87, 0x48, 0xD1, 0x78, 0x87, 0xC3, 0x23, 0xEC, 0x13, 0x6D, 0x09, 0xA1, 0xA4, 0xBB, 0x73,
0xB6, 0x95, 0xC9, 0x9C, 0x91, 0x84, 0x17, 0xE2, 0xD0, 0xAA, 0x93, 0x23, 0xA5, 0xF8, 0xD0, 0x9D,
0x75, 0xA5, 0xDF, 0x0A, 0xD2, 0x59, 0x1D, 0x8E, 0xC7, 0xED, 0x21, 0x2E, 0x30, 0xA6, 0xF6, 0xD3,
0x7A, 0x33, 0x5A, 0xFA, 0xB5, 0xFF, 0x00, 0x40, 0xF8, 0x2E, 0xD3, 0xB4, 0x7A, 0xAE, 0xDE, 0xB3,
0x1C, 0x07, 0x93, 0x7B, 0x5A, 0xE5, 0xE7, 0x1D, 0x9A, 0xA6, 0x92, 0x59, 0x2F, 0x5E, 0x8E, 0x19,
0xBB, 0xF5, 0x2D, 0x24, 0x92, 0x5E, 0xBD, 0x1A, 0x6E, 0xCD, 0x73, 0x98, 0xAA, 0x9E, 0x24, 0xEF,
0xA0, 0x76, 0x6D, 0xF4, 0x78, 0xA8, 0x74, 0x9D, 0x17, 0x89, 0x95, 0xCA, 0x59, 0xF0, 0x84, 0x36,
0x5E, 0x83, 0xEE, 0xDF, 0x47, 0x8A, 0x9D, 0x41, 0xC4, 0x3B, 0xAB, 0xC0, 0xA2, 0x55, 0xAF, 0x7B,
0x70, 0x1C, 0x7A, 0xDC, 0x23, 0x3D, 0xDB, 0xBF, 0x3C, 0x9C, 0x1A, 0xEC, 0xF2, 0xCF, 0x76, 0xEC,
0x6B, 0xBF, 0x25, 0x2B, 0x1F, 0x60, 0xE3, 0xD9, 0x4F, 0x82, 0x7D, 0xD8, 0x8B, 0x18, 0xF9, 0x1D,
0x1C, 0xD0, 0x85, 0x63, 0x02, 0x91, 0x83, 0x5F, 0x47, 0xE5, 0x33, 0x1B, 0x94, 0x6B, 0x9C, 0x4A,
0x31, 0x60, 0xB5, 0xF7, 0xDB, 0x17, 0xF9, 0x19, 0xE8, 0xA6, 0x84, 0x2B, 0x54, 0xCB, 0x43, 0x45,
0x3E, 0x92, 0xED, 0xC8, 0x56, 0x95, 0xC9, 0xB1, 0xCE, 0x70, 0xBC, 0x61, 0x03, 0x76, 0xD2, 0x5C,
0x53, 0x3A, 0x2E, 0x9B, 0xC2, 0x39, 0xCE, 0x17, 0x8C, 0x20, 0x36, 0x92, 0xE2, 0x74, 0x5D, 0x37,
0x85, 0x8F, 0xA4, 0x12, 0x98, 0x91, 0xC9, 0xB5, 0x2E, 0xA5, 0x78, 0x71, 0xDE, 0xDB, 0xBB, 0xD1,
0xA7, 0x4B, 0x34, 0x23, 0x4A, 0x29, 0xD9, 0xFA, 0x29, 0xF4, 0x77, 0xAF, 0xC2, 0x95, 0xA6, 0x6C,
0x72, 0x32, 0x3D, 0x83, 0x4A, 0x93, 0xE9, 0x11, 0x9E, 0xC0, 0x1E, 0x8F, 0x44, 0x66, 0xC6, 0x83,
0xCB, 0x39, 0x53, 0xC9, 0x6B, 0x1E, 0xAF, 0x0E, 0x2D, 0xF6, 0xC5, 0x7F, 0xEE, 0x40, 0x3D, 0x1F,
0x3E, 0x53, 0x3C, 0x75, 0xAF, 0x48, 0x03, 0x9F, 0x29, 0x9E, 0x3A, 0xD7, 0xA4, 0x07, 0x3D, 0x1D,
0xAE, 0x60, 0x00, 0x01, 0xD5, 0x34, 0x23, 0xE8, 0xA4, 0x2F, 0x3B, 0x9F, 0x98, 0xA1, 0xCB, 0xA4,
0xF7, 0xA2, 0xDF, 0x2E, 0xE6, 0xF0, 0xC1, 0x90, 0x00, 0x00, 0xE1, 0x85, 0xB0, 0x85, 0x56, 0xF8,
0x00, 0x7D, 0x00, 0x05, 0x77, 0xA6, 0x35, 0x69, 0xFE, 0x1C, 0x58, 0xCD, 0xB8, 0xB1, 0x3D, 0xA4,
0x00, 0x00, 0x45, 0x7B, 0x80, 0x08, 0xDE, 0x01, 0xA8, 0xDE, 0x00, 0x5E, 0xE0, 0x10, 0x00, 0x00,
0x68, 0xED, 0x73, 0x00, 0x00, 0x0E, 0xA9, 0xA1, 0x1F, 0x45, 0x21, 0x79, 0xDC, 0xFC, 0xC5, 0x0E,
0x5D, 0x27, 0xBD, 0x16, 0xF9, 0x77, 0x37, 0x86, 0x0C, 0x80, 0x00, 0x07, 0x0C, 0x2D, 0x84, 0x2A,
0xB7, 0xC0, 0x03, 0xE8, 0x00, 0x2B, 0xBD, 0x31, 0xAB, 0x4F, 0xF0, 0xE2, 0xC6, 0x6D, 0xC5, 0x89,
0xED, 0x20, 0x00, 0x02, 0x2B, 0xDC, 0x00, 0x46, 0xF0, 0x0D, 0x46, 0xF0, 0x02, 0xF7, 0x00, 0x80,
0x00, 0x03, 0x47, 0x6B, 0x98, 0x00, 0x00, 0x75, 0x4D, 0x08, 0xFA, 0x29, 0x0B, 0xCE, 0xE7, 0xE6,
0x28, 0x72, 0xE9, 0x3D, 0xE8, 0xB7, 0xCB, 0xB9, 0xBC, 0x30, 0x64, 0x00, 0x00, 0x38, 0x61, 0x6C,
0x21, 0x55, 0xBE, 0x00, 0x1F, 0x40, 0x01, 0x5D, 0xE9, 0x8D, 0x5A, 0x7F, 0x87, 0x16, 0x33, 0x6E,
0x2C, 0x4F, 0x69, 0x00, 0x00, 0x11, 0x5E, 0xE0, 0x02, 0x37, 0x80, 0x6A, 0x37, 0x80, 0x17, 0xB8,
0x04, 0x00, 0x00, 0x1A, 0x3B, 0x5C, 0xC0, 0x00, 0x03, 0xAA, 0x68, 0x47, 0xD1, 0x48, 0x5E, 0x77,
0x3F, 0x31, 0x43, 0x97, 0x49, 0xEF, 0x45, 0xBE, 0x5D, 0xCD, 0xE1, 0x83, 0x20, 0x00, 0x01, 0xC3,
0x0B, 0x61, 0x0A, 0xAD, 0xF0, 0x00, 0xFA, 0x00, 0x0A, 0xEF, 0x4C, 0x6A, 0xD3, 0xFC, 0x38, 0xB1,
0x9B, 0x71, 0x62, 0x7B, 0x48, 0x00, 0x00, 0x8A, 0xF7, 0x00, 0x11, 0xBC, 0x03, 0x51, 0xBC, 0x00,
0xBD, 0xC0, 0x20, 0x00, 0x01, 0xFF, 0xD9
};
static uint8_t gimage_id; // Global image ID (for all image threads)
mutex_t camera_mtx;
void encode_ssdv(const uint8_t *image, uint32_t image_len, module_conf_t* conf, uint8_t image_id, bool redudantTx)
{
ssdv_t ssdv;
uint8_t pkt[SSDV_PKT_SIZE];
uint8_t pkt_base91[BASE91LEN(SSDV_PKT_SIZE-37)];
const uint8_t *b;
uint32_t bi = 0;
uint8_t c = SSDV_OK;
uint16_t i = 0;
// Init SSDV (FEC at 2FSK, non FEC at APRS)
bi = 0;
ssdv_enc_init(&ssdv, SSDV_TYPE_NORMAL, conf->ssdv_conf.callsign, image_id, conf->ssdv_conf.quality);
ssdv_enc_set_buffer(&ssdv, pkt);
// Init transmission packet
radioMSG_t msg;
msg.bin_len = 0;
msg.freq = getFrequency(&conf->frequency);
msg.power = conf->power;
//image_id = 0; // FIXME temporary
ax25_t ax25_handle;
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
msg.mod = conf->protocol == PROT_APRS_AFSK ? MOD_AFSK : MOD_2GFSK;
msg.afsk_conf = &(conf->afsk_conf);
msg.gfsk_conf = &(conf->gfsk_conf);
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
}
while(true)
{
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
while((c = ssdv_enc_get_packet(&ssdv)) == SSDV_FEED_ME)
{
b = &image[bi];
uint8_t r = bi < image_len-128 ? 128 : image_len - bi;
bi += r;
if(r <= 0)
{
TRACE_ERROR("SSDV > Premature end of file");
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle);
if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
msg.bin_len = 0;
}
break;
}
ssdv_enc_feed(&ssdv, b, r);
}
if(c == SSDV_EOI)
{
TRACE_INFO("SSDV > ssdv_enc_get_packet said EOI");
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle);
if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
msg.bin_len = 0;
}
break;
} else if(c != SSDV_OK) {
TRACE_ERROR("SSDV > ssdv_enc_get_packet failed: %i", c);
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK) msg.bin_len = aprs_encode_data_finalize(&ax25_handle);
if(msg.bin_len > 0) transmitOnRadio(&msg, false); // Empty buffer
if(conf->protocol == PROT_APRS_2GFSK || conf->protocol == PROT_APRS_AFSK)
{
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
msg.bin_len = 0;
}
return;
}
switch(conf->protocol) {
case PROT_APRS_2GFSK:
case PROT_APRS_AFSK:
// Encode packet
TRACE_INFO("IMG > Encode APRS/SSDV packet");
base91_encode(&pkt[1], pkt_base91, sizeof(pkt)-37); // Sync byte, CRC and FEC of SSDV not transmitted
msg.bin_len = aprs_encode_data_encodePacket(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91));
if(redudantTx)
msg.bin_len = aprs_encode_data_encodePacket(&ax25_handle, 'I', &conf->aprs_conf, pkt_base91, strlen((char*)pkt_base91));
// Transmit
if(msg.bin_len >= 58000 || conf->packet_spacing) // Transmit if buffer is almost full or if single packet transmission is activated (packet_spacing != 0)
{
// Transmit packets
msg.bin_len = aprs_encode_data_finalize(&ax25_handle);
transmitOnRadio(&msg, false);
// Initialize new packet buffer
aprs_encode_data_init(&ax25_handle, msg.msg, msg.mod);
msg.bin_len = 0;
//chThdSleepMilliseconds(8000); // FIXME: Throttle it for my poor TH-D72. Has to be removed later.
}
break;
case PROT_SSDV_2FSK:
msg.mod = MOD_2FSK;
msg.fsk_conf = &(conf->fsk_conf);
// Encode packet
TRACE_INFO("IMG > Encode 2FSK/SSDV packet");
memcpy(&msg.msg[msg.bin_len/8], pkt, sizeof(pkt));
msg.bin_len += 8*sizeof(pkt);
if(redudantTx)
{
memcpy(&msg.msg[msg.bin_len/8], pkt, sizeof(pkt));
msg.bin_len += 8*sizeof(pkt);
}
// Transmit
if(msg.bin_len >= 61440 || conf->packet_spacing) { // Transmit if buffer is full or if single packet transmission activation (packet_spacing != 0)
// Transmit packets
transmitOnRadio(&msg, false);
// Initialize new packet buffer
msg.bin_len = 0;
}
break;
default:
TRACE_ERROR("IMG > Unsupported protocol selected for module IMAGE");
}
// Packet spacing (delay)
if(conf->packet_spacing)
chThdSleepMilliseconds(conf->packet_spacing);
i++;
}
}
THD_FUNCTION(imgThread, arg) {
module_conf_t* conf = (module_conf_t*)arg;
bool camInitialized = false;
systime_t time = chVTGetSystemTimeX();
while(true)
{
TRACE_INFO("IMG > Do module IMAGE cycle");
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(600); // TODO: Implement more sophisticated method
if(!p_sleep(&conf->sleep_conf))
{
uint32_t image_len = 0;
uint8_t *image;
// Lock camera FIXME: Removed for testing
TRACE_INFO("IMG > Lock camera");
chMtxLock(&camera_mtx);
uint8_t tries;
bool status = false;
bool camera_found = false;
// Detect camera
if(camInitialized || OV5640_isAvailable()) { // OV5640 available
TRACE_INFO("IMG > OV5640 found");
camera_found = true;
if(conf->ssdv_conf.res == RES_MAX) // Attempt maximum resolution (limited by memory)
{
conf->ssdv_conf.res = RES_UXGA; // Try maximum resolution
do {
// Init camera
if(!camInitialized) {
OV5640_init(&conf->ssdv_conf);
camInitialized = true;
}
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
lockRadio(); // Lock radio
status = OV5640_Snapshot2RAM();
unlockRadio(); // Unlock radio
} while(!status && --tries);
conf->ssdv_conf.res--; // Decrement resolution in next attempt (if status==false)
} while(OV5640_BufferOverflow() && conf->ssdv_conf.res >= RES_QVGA);
conf->ssdv_conf.res = RES_MAX; // Revert register
} else { // Static resolution
// Init camera
if(!camInitialized) {
OV5640_init(&conf->ssdv_conf);
camInitialized = true;
}
// Sample data from DCMI through DMA into RAM
tries = 5; // Try 5 times at maximum
do { // Try capturing image until capture successful
status = OV5640_Snapshot2RAM();
} while(!status && --tries);
}
// Switch off camera
if(!keep_cam_switched_on) {
OV5640_deinit();
camInitialized = false;
}
// Get image
image_len = OV5640_getBuffer(&image);
TRACE_INFO("IMG > Image size: %d bytes", image_len);
} else { // Camera error
camInitialized = false;
TRACE_ERROR("IMG > No camera found");
}
// Unlock camera FIXME: Removed for testing
TRACE_INFO("IMG > Unlock camera");
chMtxUnlock(&camera_mtx);
// Encode/Transmit SSDV if image sampled successfully
if(status) {
gimage_id++;
TRACE_INFO("IMG > Encode/Transmit SSDV ID=%d", gimage_id-1);
encode_ssdv(image, image_len, conf, gimage_id-1, conf->ssdv_conf.redundantTx);
} else if(!camera_found) { // No camera found
gimage_id++;
TRACE_INFO("IMG > Encode/Transmit SSDV (no cam found) ID=%d", gimage_id-1);
encode_ssdv(noCameraFound, sizeof(noCameraFound), conf, gimage_id-1, conf->ssdv_conf.redundantTx);
}
}
time = waitForTrigger(time, &conf->trigger);
}
}
void start_image_thread(module_conf_t *conf)
{
if(conf->init_delay) chThdSleepMilliseconds(conf->init_delay);
TRACE_INFO("IMG > Startup image thread");
chsnprintf(conf->name, sizeof(conf->name), "IMG");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(100*1024), "IMG", NORMALPRIO, imgThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("IMG > Could not startup thread (not enough memory available)");
} else {
register_thread_at_wdg(conf);
conf->wdg_timeout = chVTGetSystemTimeX() + S2ST(1);
}
}

Wyświetl plik

@ -7,7 +7,7 @@
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "threads.h"
#include "base.h"
#include "aprs.h"
#include "flash.h"

Wyświetl plik

@ -2,7 +2,7 @@
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "threads.h"
#include "config.h"
#include "radio.h"
#include "aprs.h"
@ -93,8 +93,8 @@ void replace_placeholders(char* fskmsg, uint16_t size, trackPoint_t *tp) {
str_replace(fskmsg, size, "<VSOL>", buf);
chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_pbat/1000, (tp->adc_pbat >= 0 ? 1 : -1) * (tp->adc_pbat%1000));
str_replace(fskmsg, size, "<PBAT>", buf);
chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_isol/1000, (tp->adc_isol >= 0 ? 1 : -1) * (tp->adc_isol%1000));
str_replace(fskmsg, size, "<ISOL>", buf);
chsnprintf(buf, sizeof(buf), "%d.%03d", tp->adc_rbat/1000, (tp->adc_rbat >= 0 ? 1 : -1) * (tp->adc_rbat%1000));
str_replace(fskmsg, size, "<RBAT>", buf);
chsnprintf(buf, sizeof(buf), "%d", tp->air_press/10);
str_replace(fskmsg, size, "<PRESS>", buf);
chsnprintf(buf, sizeof(buf), "%d.%d", tp->air_temp/100, (tp->air_temp%100)/10);
@ -218,7 +218,7 @@ void start_position_thread(module_conf_t *conf)
// Start position thread
TRACE_INFO("POS > Startup position thread");
chsnprintf(conf->name, sizeof(conf->name), "POS");
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(2*1024), "POS", NORMALPRIO, posThread, conf);
thread_t *th = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(10*1024), "POS", NORMALPRIO, posThread, conf);
if(!th) {
// Print startup error, do not start watchdog for this thread
TRACE_ERROR("POS > Could not startup thread (not enough memory available)");

Wyświetl plik

@ -2,13 +2,12 @@
#include "hal.h"
#include "debug.h"
#include "modules.h"
#include "threads.h"
#include "tracking.h"
#include "watchdog.h"
#include "pi2c.h"
#include "pac1720.h"
mutex_t interference_mtx;
systime_t watchdog_tracking;
void start_essential_threads(void) {
@ -16,7 +15,6 @@ void start_essential_threads(void) {
init_watchdog();
// Initialize essential mutex's
chMtxObjectInit(&interference_mtx);
chMtxObjectInit(&camera_mtx);
chMtxObjectInit(&radio_mtx);

Wyświetl plik

@ -11,7 +11,6 @@
void start_essential_threads(void);
extern mutex_t interference_mtx; // HF interference mutex (needed to exclude radio from HF sensitiv components [Camera])
extern systime_t watchdog_tracking; // Last update time for module TRACKING
#endif

Wyświetl plik

@ -179,7 +179,7 @@ THD_FUNCTION(trackingThread, arg) {
lastTrackPoint->gps_alt = lastLogPoint.gps_alt;
}
lastTrackPoint->gps_lock = 0; // But tell the user that there is no current lock nor any GPS sats locked
lastTrackPoint->gps_lock = GPS_LOSS; // But tell the user that there is no current lock nor any GPS sats locked
lastTrackPoint->gps_sats = 0;
lastTrackPoint->gps_ttff = 0;
@ -202,7 +202,6 @@ THD_FUNCTION(trackingThread, arg) {
lastTrackPoint->adc_vsol = getSolarVoltageMV();
lastTrackPoint->adc_vbat = getBatteryVoltageMV();
lastTrackPoint->adc_vusb = getUSBVoltageMV();
lastTrackPoint->adc_isol = pac1720_getIsol();
lastTrackPoint->adc_pbat = pac1720_getPbat();
bme280_t bme280;
@ -234,7 +233,7 @@ THD_FUNCTION(trackingThread, arg) {
// Switch on GPS is enough power is available
uint16_t batt = getBatteryVoltageMV();
if(batt >= GPS_ON_VBAT)
if(batt >= gps_on_vbat)
{
// Switch on GPS
GPS_Init();
@ -243,22 +242,24 @@ THD_FUNCTION(trackingThread, arg) {
do {
batt = getBatteryVoltageMV();
gps_get_fix(&gpsFix);
} while(!isGPSLocked(&gpsFix) && batt >= GPS_OFF_VBAT && chVTGetSystemTimeX() <= time + S2ST(TRACK_CYCLE_TIME-5)); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-1sec in order to keep synchronization)
} while(!isGPSLocked(&gpsFix) && batt >= gps_off_vbat && chVTGetSystemTimeX() <= time + track_cycle_time - S2ST(3)); // Do as long no GPS lock and within timeout, timeout=cycle-1sec (-3sec in order to keep synchronization)
if(batt < GPS_OFF_VBAT) // Switch off GPS at low batt
if(batt < gps_off_vbat) // Switch off GPS at low batt
GPS_Deinit();
}
if(isGPSLocked(&gpsFix)) { // GPS locked
// Switch off GPS (if cycle time is more than 60 seconds)
#if TRACK_CYCLE_TIME > 60
GPS_Deinit();
#endif
if(track_cycle_time >= S2ST(60)) {
TRACE_INFO("TRAC > Switch off GPS");
GPS_Deinit();
} else {
TRACE_INFO("TRAC > Keep GPS switched of because cycle < 60sec");
}
// Debug
TRACE_INFO("TRAC > GPS sampling finished GPS LOCK");
TRACE_GPSFIX(&gpsFix);
// Calibrate RTC
setTime(gpsFix.time);
@ -276,13 +277,17 @@ THD_FUNCTION(trackingThread, arg) {
tp->gps_lon = gpsFix.lon;
tp->gps_alt = gpsFix.alt;
tp->gps_lock = isGPSLocked(&gpsFix);
tp->gps_lock = GPS_LOCKED;
tp->gps_sats = gpsFix.num_svs;
} else { // GPS lost (keep GPS switched on)
// Debug
TRACE_WARN("TRAC > GPS sampling finished GPS LOSS");
if(batt < gps_off_vbat) {
TRACE_WARN("TRAC > GPS sampling finished GPS LOW BATT");
} else {
TRACE_WARN("TRAC > GPS sampling finished GPS LOSS");
}
// Take time from internal RTC
getTime(&rtc);
@ -298,8 +303,8 @@ THD_FUNCTION(trackingThread, arg) {
tp->gps_lon = ltp->gps_lon;
tp->gps_alt = ltp->gps_alt;
// Mark gpsloss
tp->gps_lock = false;
// Mark GPS loss (or low batt)
tp->gps_lock = batt < gps_off_vbat ? GPS_LOWBATT : GPS_LOSS;
tp->gps_sats = 0;
}
@ -311,8 +316,8 @@ THD_FUNCTION(trackingThread, arg) {
tp->adc_vsol = getSolarVoltageMV();
tp->adc_vbat = getBatteryVoltageMV();
tp->adc_vusb = getUSBVoltageMV();
tp->adc_isol = pac1720_getIsol();
tp->adc_pbat = pac1720_getAvgPbat();
tp->adc_rbat = pac1720_getAvgRbat();
bme280_t bme280;
@ -332,15 +337,15 @@ THD_FUNCTION(trackingThread, arg) {
// Trace data
TRACE_INFO( "TRAC > New tracking point available (ID=%d)\r\n"
"%s Time %04d-%02d-%02d %02d:%02d:%02d\r\n"
"%s Pos %d.%07d %d.%07d Alt %dm\r\n"
"%s Pos %d.%05d %d.%05d Alt %dm\r\n"
"%s Sats %d TTFF %dsec\r\n"
"%s ADC Vbat=%d.%03dV Vsol=%d.%03dV VUSB=%d.%03dV Pbat=%dmW Isol=%dmA\r\n"
"%s ADC Vbat=%d.%03dV Vsol=%d.%03dV VUSB=%d.%03dV Pbat=%dmW Rbat=%dmOhm\r\n"
"%s AIR p=%6d.%01dPa T=%2d.%02ddegC phi=%2d.%01d%%",
tp->id,
TRACE_TAB, tp->time.year, tp->time.month, tp->time.day, tp->time.hour, tp->time.minute, tp->time.day,
TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*tp->gps_lat%10000000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*tp->gps_lon%10000000, tp->gps_alt,
TRACE_TAB, tp->gps_lat/10000000, (tp->gps_lat > 0 ? 1:-1)*(tp->gps_lat/100)%100000, tp->gps_lon/10000000, (tp->gps_lon > 0 ? 1:-1)*(tp->gps_lon/100)%100000, tp->gps_alt,
TRACE_TAB, tp->gps_sats, tp->gps_ttff,
TRACE_TAB, tp->adc_vbat/1000, (tp->adc_vbat%1000), tp->adc_vsol/1000, (tp->adc_vsol%1000), tp->adc_vusb/1000, (tp->adc_vusb%1000), tp->adc_pbat, tp->adc_isol,
TRACE_TAB, tp->adc_vbat/1000, (tp->adc_vbat%1000), tp->adc_vsol/1000, (tp->adc_vsol%1000), tp->adc_vusb/1000, (tp->adc_vusb%1000), tp->adc_pbat, tp->adc_rbat,
TRACE_TAB, tp->air_press/10, tp->air_press%10, tp->air_temp/100, tp->air_temp%100, tp->air_hum/10, tp->air_hum%10
);
@ -348,14 +353,14 @@ THD_FUNCTION(trackingThread, arg) {
if(nextLogEntryTimer <= chVTGetSystemTimeX() && isGPSLocked(&gpsFix))
{
writeLogTrackPoint(tp);
nextLogEntryTimer += S2ST(LOG_CYCLE_TIME);
nextLogEntryTimer += log_cycle_time;
}
// Switch last recent track point
lastTrackPoint = tp;
id++;
time = chThdSleepUntilWindowed(time, time + S2ST(TRACK_CYCLE_TIME)); // Wait until time + cycletime
time = chThdSleepUntilWindowed(time, time + track_cycle_time); // Wait until time + cycletime
}
}

Wyświetl plik

@ -5,12 +5,16 @@
#include "hal.h"
#include "ptime.h"
#define GPS_LOCKED 0 /* GPS is locked and could aquire a fix */
#define GPS_LOSS 1 /* GPS was switched on all time but it couln't aquire a fix */
#define GPS_LOWBATT 2 /* GPS was switched on but had to be switched off prematurely while the battery is almost empty (or is too cold) */
typedef struct {
uint32_t id; // Serial ID
ptime_t time; // GPS time
// GPS
bool gps_lock; // True = on last try GPS has been locked
uint8_t gps_lock; // 0: locked, 1: GPS loss, 2: low power (switched off)
int32_t gps_lat; // Latitude in °*10^7
int32_t gps_lon; // Longitude in °*10^7
int32_t gps_alt; // Altitude in meter
@ -22,7 +26,7 @@ typedef struct {
uint16_t adc_vbat; // Current battery voltage in mV
uint16_t adc_vusb; // Current USB voltage in mV
int16_t adc_pbat; // Average battery current (since last track point)
int16_t adc_isol; // Current solar shot current
int16_t adc_rbat; // Battery impedance
// BME280 (on board)
uint32_t air_press; // Airpressure in Pa*10 (in 0.1Pa)

Wyświetl plik

@ -31,7 +31,7 @@ typedef enum {
TEL_VBAT,
TEL_VSOL,
TEL_PBAT,
TEL_ISOL,
TEL_RBAT,
TEL_PRESS,
TEL_TEMP,
TEL_HUM
@ -52,9 +52,9 @@ typedef struct {
typedef enum {
SLEEP_DISABLED,
SLEEP_WHEN_VBAT_BELOW_THRES,
SLEEP_WHEN_ISOL_BELOW_THRES,
SLEEP_WHEN_RBAT_BELOW_THRES,
SLEEP_WHEN_VBAT_ABOVE_THRES,
SLEEP_WHEN_ISOL_ABOVE_THRES,
SLEEP_WHEN_RBAT_ABOVE_THRES,
SLEEP_WHEN_DISCHARGING,
SLEEP_WHEN_CHARGING
} sleep_type_t;
@ -62,7 +62,7 @@ typedef enum {
typedef struct {
sleep_type_t type;
uint16_t vbat_thres;
uint16_t isol_thres;
uint16_t rbat_thres;
} sleep_conf_t;
typedef struct {
@ -86,7 +86,7 @@ typedef struct {
} gfsk_conf_t;
typedef struct { // Radio message type
uint8_t msg[512]; // Message (data)
uint8_t msg[8191]; // Message (data)
uint32_t bin_len; // Binary length
uint32_t freq; // Frequency
int8_t power; // Power in dBm
@ -99,7 +99,7 @@ typedef struct { // Radio message type
} radioMSG_t;
typedef enum {
RES_QCIF,
RES_QQVGA,
RES_QVGA,
RES_VGA,
RES_XGA,
@ -108,12 +108,12 @@ typedef enum {
} resolution_t;
typedef struct {
char callsign[8]; // Callsign
char callsign[7]; // Callsign (or stream identifier)
resolution_t res; // Camera resolution
uint8_t quality; // JPEG quality
uint8_t *ram_buffer; // Camera Buffer
uint16_t ram_size; // Size of buffer
uint16_t size_sampled; // Actual image data size (do not set in config)
uint32_t ram_size; // Size of buffer
uint32_t size_sampled; // Actual image data size (do not set in config)
bool redundantTx; // Redundand packet transmission (APRS only)
} ssdv_conf_t;
@ -131,7 +131,7 @@ typedef enum {
TRIG_ONCE, // Trigger once and never again (e.g. transmit specific position packet only at startup)
TRIG_NEW_POINT, // Triggered when new track point available
TRIG_TIMEOUT, // Triggered by timeout (e.g. trasmit position every 120sec)
TRIG_CONTINOUSLY // Continue continously (e.g. send new image once old image sent completely)
TRIG_CONTINUOUSLY // Continue continuously (e.g. send new image once old image sent completely)
} trigger_type_t;
typedef struct {