Add FPGA-driven video using PIO/DMA

This commit is contained in:
shylie 2024-09-26 10:28:41 -04:00
parent 1ca3d882a7
commit b725311229
16 changed files with 439 additions and 63 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
build/ build/
.idea/ .idea/
ice/mandelbrot/impl_1/

View File

@ -8,9 +8,12 @@ pico_sdk_init()
add_subdirectory(pico-ice-sdk) add_subdirectory(pico-ice-sdk)
add_executable(pico-ice-video main.cpp usb_descriptors.c) add_executable(pico-ice-video main.cpp usb_descriptors.c)
pico_generate_pio_header(pico-ice-video ${CMAKE_CURRENT_LIST_DIR}/fpga.pio)
target_link_libraries(pico-ice-video pico_ice_sdk pico_ice_usb pico_stdio_usb pico_bootsel_via_double_reset) target_link_libraries(pico-ice-video pico_ice_sdk pico_ice_usb pico_stdio_usb pico_bootsel_via_double_reset)
target_include_directories(pico-ice-video PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_BINARY_DIR}) target_include_directories(pico-ice-video PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_BINARY_DIR})
pico_add_extra_outputs(pico-ice-video) pico_add_extra_outputs(pico-ice-video)
pico_enable_stdio_usb(pico-ice-video 0) pico_enable_stdio_usb(pico-ice-video 1)
pico_enable_stdio_uart(pico-ice-video 0) pico_enable_stdio_uart(pico-ice-video 0)

57
fpga.pio Normal file
View File

@ -0,0 +1,57 @@
.pio_version 0
.program fpga
.fifo rx
set x, 8
.wrap_target
in x, 4
in null, 4
push
in pins, 8 [1]
push
in x, 4
in null, 4
push
in pins, 8 [1]
push
.wrap
% c-sdk {
#include <hardware/dma.h>
#ifndef DMA_CHANNEL
#define DMA_CHANNEL 0
#endif//DMA_CHANNEL
static uint8_t frame_buffer[2][FRAME_WIDTH * FRAME_HEIGHT * 16 / 8] = { };
static bool current_frame = false;
static inline void fpga_program_init(PIO pio, uint sm, uint offset, uint pin_base, float div)
{
{
pio_sm_config c = fpga_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin_base);
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, 8, false);
sm_config_set_in_shift(&c, false, false, 8);
pio_sm_init(pio, sm, offset, &c);
}
{
dma_channel_config c = dma_channel_get_default_config(DMA_CHANNEL);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));
dma_channel_configure(DMA_CHANNEL, &c, NULL, &pio->rxf[sm], FRAME_WIDTH * FRAME_HEIGHT * 2 + 1, false);
}
}
%}

11
ice/constraints.pdc Normal file
View File

@ -0,0 +1,11 @@
ldc_set_location -site 35 [get_ports {clk}]
ldc_set_location -site 14 [get_ports {start}]
ldc_set_location -site 27 [get_ports {data[0]}]
ldc_set_location -site 25 [get_ports {data[1]}]
ldc_set_location -site 21 [get_ports {data[2]}]
ldc_set_location -site 19 [get_ports {data[3]}]
ldc_set_location -site 26 [get_ports {data[4]}]
ldc_set_location -site 23 [get_ports {data[5]}]
ldc_set_location -site 20 [get_ports {data[6]}]
ldc_set_location -site 18 [get_ports {data[7]}]

1
ice/constraints.sdc Normal file
View File

@ -0,0 +1 @@
create_clock -period 20.8333 [get_ports {clk}]

17
ice/mandelbrot/.recovery Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<RadiantProject version="4.2" radiant="2024.1.0.34.2" title="mandelbrot" device="iCE40UP5K-SG48I" performance_grade="High-Performance_1.2V" family_int="ice40tp" device_int="itpa08" package_int="SG48" operation_int="IND" speed_int="6" default_implementation="impl_1">
<Options/>
<Implementation title="impl_1" dir="impl_1" description="impl_1" synthesis="synplify" default_strategy="Strategy1">
<Options def_top="top"/>
<Source name="source/impl_1/top.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog" top_module="top"/>
</Source>
<Source name="../constraints.pdc" type="Physical Constraints File" type_short="PDC">
<Options/>
</Source>
<Source name="../constraints.sdc" type="Pre-Synthesis Constraints File" type_short="SDC">
<Options/>
</Source>
</Implementation>
<Strategy name="Strategy1" file="mandelbrot1.sty"/>
</RadiantProject>

6
ice/mandelbrot/drc.log Normal file
View File

@ -0,0 +1,6 @@
DRC: Design Rule Check Radiant Software (64-bit) 2024.1.0.34.2
Wed Sep 25 13:18:59 2024
WARNING <71003020> - Top module port 'start' does not connect to anything.
ERROR <71003010> - The port [clk] is assigned to a nonexistent pin [35]. Reassign the port to a valid pin.
ERROR <71003010> - The port [data[0]] is assigned to a nonexistent pin [27]. Reassign the port to a valid pin.

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<RadiantProject version="4.2" radiant="2024.1.0.34.2" title="mandelbrot" device="iCE40UP5K-UWG30ITR" performance_grade="High-Performance_1.2V" family_int="ice40tp" device_int="itpa08" package_int="UWG30" operation_int="IND" speed_int="6" default_implementation="impl_1">
<Options/>
<Implementation title="impl_1" dir="impl_1" description="impl_1" synthesis="synplify" default_strategy="Strategy1">
<Options/>
<Source name="../top.sv" type="Verilog" type_short="Verilog">
<Options VerilogStandard="System Verilog"/>
</Source>
<Source name="../constraints.pdc" type="Physical Constraints File" type_short="PDC">
<Options/>
</Source>
<Source name="../constraints.sdc" type="Pre-Synthesis Constraints File" type_short="SDC">
<Options/>
</Source>
</Implementation>
<Strategy name="Strategy1" file="mandelbrot1.sty"/>
</RadiantProject>

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE strategy>
<Strategy version="1.0" predefined="0" description="" label="Strategy1">
<Property name="PROP_BD_EdfHardtimer" value="Enable" time="0"/>
<Property name="PROP_BD_EdfInBusNameConv" value="None" time="0"/>
<Property name="PROP_BD_EdfInLibPath" value="" time="0"/>
<Property name="PROP_BD_EdfInRemLoc" value="Off" time="0"/>
<Property name="PROP_BD_EdfMemPath" value="" time="0"/>
<Property name="PROP_BD_ParSearchPath" value="" time="0"/>
<Property name="PROP_BIT_CmdLineArgs" value="" time="0"/>
<Property name="PROP_BIT_INITEBR0" value="True" time="0"/>
<Property name="PROP_BIT_INITEBR1" value="True" time="0"/>
<Property name="PROP_BIT_INITEBR2" value="True" time="0"/>
<Property name="PROP_BIT_INITEBR3" value="True" time="0"/>
<Property name="PROP_BIT_NVCMSecurity" value="False" time="0"/>
<Property name="PROP_BIT_NoHeader" value="False" time="0"/>
<Property name="PROP_BIT_NoPullup" value="False" time="0"/>
<Property name="PROP_BIT_OSCFREQ" value="Slow" time="0"/>
<Property name="PROP_BIT_OutFormatBitGen" value="bin" time="0"/>
<Property name="PROP_BIT_RunDRCBitGen" value="True" time="0"/>
<Property name="PROP_BIT_SPILowPower" value="False" time="0"/>
<Property name="PROP_BIT_WarmBoot" value="False" time="0"/>
<Property name="PROP_CPE_IPDebugMode" value="False" time="0"/>
<Property name="PROP_IOTIMING_AllSpeed" value="False" time="0"/>
<Property name="PROP_LST_AllowDUPMod" value="False" time="0"/>
<Property name="PROP_LST_AllowMixedAssignments" value="False" time="0"/>
<Property name="PROP_LST_CarryChain" value="True" time="0"/>
<Property name="PROP_LST_CarryChainLength" value="0" time="0"/>
<Property name="PROP_LST_CmdLineArgs" value="" time="0"/>
<Property name="PROP_LST_DSPStyle" value="DSP" time="0"/>
<Property name="PROP_LST_DSPUtil" value="100" time="0"/>
<Property name="PROP_LST_DecodeUnreachableStates" value="False" time="0"/>
<Property name="PROP_LST_EBRUtil" value="100" time="0"/>
<Property name="PROP_LST_EdfFrequency" value="200" time="0"/>
<Property name="PROP_LST_EdfInLibPath" value="" time="0"/>
<Property name="PROP_LST_EdfInRemLoc" value="Off" time="0"/>
<Property name="PROP_LST_EdfMemPath" value="" time="0"/>
<Property name="PROP_LST_FIXGATEDCLKS" value="True" time="0"/>
<Property name="PROP_LST_FSMEncodeStyle" value="Auto" time="0"/>
<Property name="PROP_LST_IOInsertion" value="True" time="0"/>
<Property name="PROP_LST_IgnoreSDCError" value="False" time="0"/>
<Property name="PROP_LST_InterFileDump" value="False" time="0"/>
<Property name="PROP_LST_LoopLimit" value="1950" time="0"/>
<Property name="PROP_LST_MaxFanout" value="1000" time="0"/>
<Property name="PROP_LST_OptimizeGoal" value="Area" time="0"/>
<Property name="PROP_LST_PropagatConst" value="True" time="0"/>
<Property name="PROP_LST_RAMStyle" value="Auto" time="0"/>
<Property name="PROP_LST_ROMStyle" value="Auto" time="0"/>
<Property name="PROP_LST_RWCheckOnRam" value="False" time="0"/>
<Property name="PROP_LST_RemoveDupRegs" value="True" time="0"/>
<Property name="PROP_LST_ReportTrimmedUserNets" value="False" time="0"/>
<Property name="PROP_LST_ResolvedMixedDrivers" value="False" time="0"/>
<Property name="PROP_LST_ResourceShare" value="True" time="0"/>
<Property name="PROP_LST_UseIOReg" value="Auto" time="0"/>
<Property name="PROP_LST_VHDL2008" value="False" time="0"/>
<Property name="PROP_MAPSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
<Property name="PROP_MAPSTA_CmdLineArgs" value="" time="0"/>
<Property name="PROP_MAPSTA_EndPtNumber" value="10" time="0"/>
<Property name="PROP_MAPSTA_NPerEnd" value="1" time="0"/>
<Property name="PROP_MAPSTA_NumPathsPerClock" value="10" time="0"/>
<Property name="PROP_MAPSTA_ReportFormat" value="Lattice Standard" time="0"/>
<Property name="PROP_MAPSTA_SpeedForHoldAnalysis" value="m" time="0"/>
<Property name="PROP_MAPSTA_SpeedForSetupAnalysis" value="default" time="0"/>
<Property name="PROP_MAPSTA_UnconstrainedPathsNumber" value="10" time="0"/>
<Property name="PROP_MAP_IgnoreSDCErr" value="False" time="0"/>
<Property name="PROP_MAP_MapModArgs" value="" time="0"/>
<Property name="PROP_MAP_SigCrossRef" value="False" time="0"/>
<Property name="PROP_MAP_SymCrossRef" value="False" time="0"/>
<Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
<Property name="PROP_PARSTA_CmdLineArgs" value="" time="0"/>
<Property name="PROP_PARSTA_EndPtNumber" value="10" time="0"/>
<Property name="PROP_PARSTA_NPerEnd" value="1" time="0"/>
<Property name="PROP_PARSTA_NumPathsPerClock" value="10" time="0"/>
<Property name="PROP_PARSTA_ReportFormat" value="Lattice Standard" time="0"/>
<Property name="PROP_PARSTA_SpeedForHoldAnalysis" value="m" time="0"/>
<Property name="PROP_PARSTA_SpeedForSetupAnalysis" value="default" time="0"/>
<Property name="PROP_PARSTA_UnconstrainedPathsNumber" value="10" time="0"/>
<Property name="PROP_PAR_DisableAutoHldTiming" value="False" time="0"/>
<Property name="PROP_PAR_DisableTDParDes" value="False" time="0"/>
<Property name="PROP_PAR_ImposeHoldTimeCorrect" value="False" time="0"/>
<Property name="PROP_PAR_NumOfHostMachineCores" value="1" time="0"/>
<Property name="PROP_PAR_PARModArgs" value="" time="0"/>
<Property name="PROP_PAR_PackLogicUtil" value="" time="0"/>
<Property name="PROP_PAR_ParMultiNodeList" value="" time="0"/>
<Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/>
<Property name="PROP_PAR_PlcIterParDes" value="1" time="0"/>
<Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/>
<Property name="PROP_PAR_PriHldCorrectOverSetup" value="False" time="0"/>
<Property name="PROP_PAR_SaveBestRsltParDes" value="1" time="0"/>
<Property name="PROP_PAR_SpdGradeHoldOpt" value="m" time="0"/>
<Property name="PROP_PAR_SpdGradeSetupOpt" value="Default" time="0"/>
<Property name="PROP_PAR_StopZero" value="False" time="0"/>
<Property name="PROP_PAR_parPathBased" value="On" time="0"/>
<Property name="PROP_POSTSYN_CmdLineArgs" value="" time="0"/>
<Property name="PROP_POSTSYN_ExtModuleFiles" value="" time="0"/>
<Property name="PROP_POSTSYN_IgnoreSDCErr" value="False" time="0"/>
<Property name="PROP_SYNSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
<Property name="PROP_SYNSTA_CmdLineArgs" value="" time="0"/>
<Property name="PROP_SYNSTA_EndPtNumber" value="10" time="0"/>
<Property name="PROP_SYNSTA_NPerEnd" value="1" time="0"/>
<Property name="PROP_SYNSTA_NumPathsPerClock" value="10" time="0"/>
<Property name="PROP_SYNSTA_ReportFormat" value="Lattice Standard" time="0"/>
<Property name="PROP_SYNSTA_UnconstrainedPathsNumber" value="10" time="0"/>
<Property name="PROP_SYN_ClockConversion" value="False" time="0"/>
<Property name="PROP_SYN_CmdLineArgs" value="" time="0"/>
<Property name="PROP_SYN_EdfAllowDUPMod" value="False" time="0"/>
<Property name="PROP_SYN_EdfArea" value="False" time="0"/>
<Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/>
<Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/>
<Property name="PROP_SYN_EdfFanout" value="1000" time="0"/>
<Property name="PROP_SYN_EdfFrequency" value="200" time="0"/>
<Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/>
<Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/>
<Property name="PROP_SYN_EdfNumStartEnd" value="" time="0"/>
<Property name="PROP_SYN_EdfOutNetForm" value="None" time="0"/>
<Property name="PROP_SYN_EdfPushTirstates" value="True" time="0"/>
<Property name="PROP_SYN_EdfResSharing" value="True" time="0"/>
<Property name="PROP_SYN_EdfRunRetiming" value="Pipelining Only" time="0"/>
<Property name="PROP_SYN_EdfSymFSM" value="True" time="0"/>
<Property name="PROP_SYN_EdfUnconsClk" value="False" time="0"/>
<Property name="PROP_SYN_ExportSetting" value="Yes" time="0"/>
<Property name="PROP_SYN_FDCFiles" value="" time="0"/>
<Property name="PROP_SYN_LibPath" value="" time="0"/>
<Property name="PROP_SYN_RamRWCheck" value="False" time="0"/>
<Property name="PROP_SYN_ResolvedMixedDrivers" value="False" time="0"/>
<Property name="PROP_SYN_ResynthesizeAll" value="True" time="0"/>
<Property name="PROP_SYN_UpdateCompilePtTimData" value="False" time="0"/>
<Property name="PROP_SYN_VHDL2008" value="False" time="0"/>
<Property name="PROP_TIM_MaxDelSimDes" value="" time="0"/>
<Property name="PROP_TIM_MinSpeedGrade" value="False" time="0"/>
<Property name="PROP_TIM_ModPreSimDes" value="" time="0"/>
<Property name="PROP_TIM_NegStupHldTim" value="True" time="0"/>
<Property name="PROP_TIM_TimSimGenPUR" value="True" time="0"/>
<Property name="PROP_TIM_TimSimGenX" value="False" time="0"/>
<Property name="PROP_TIM_TimSimHierSep" value="" time="0"/>
<Property name="PROP_TIM_TrgtSpeedGrade" value="High-Performance_1.2V" time="0"/>
<Property name="PROP_TMCHK_EnableCheck" value="True" time="0"/>
</Strategy>

View File

@ -0,0 +1,97 @@
<HTML>
<HEAD>
<TITLE>Lattice TCL Log</TITLE>
<link href="file:///C:/lscc/radiant/2024.1/data/theme/css/light/report.css" rel="stylesheet" type="text/css" media="screen"/>
<link href="file:///C:/lscc/radiant/2024.1/data/theme/css/print/report.css" rel="stylesheet" type="text/css" media="print"/>
<style type="text/css">
#toc {
position: fixed;
right: 2px;
top: 2px;
padding: 2px 5px 2px 5px;
background-color:rgba(210,210,210,0.1);
border-style: solid;
border-color: rgba(192,192,192,0.8);
border-width:1px;
}
#toc_list {
display: none;
}
</style>
<script type="text/javascript">
<!--
function showTocList() {
var a = document.getElementById("toc_list");
a.style.display = "block";
}
function hideTocList() {
var a = document.getElementById("toc_list");
if (a)
a.style.display = "none";
}
//-->
</script>
</HEAD>
<BODY>
<DIV id="content" onclick="hideTocList()"><PRE>
</PRE></DIV>
<button id="back_to_top" class="radiant" onclick="scrollToTop()">&lt;</button>
<script type="text/javascript">
<!--
var scrollStep = 0;
function scrollToTop(){
var funScroll = function() {
var top = document.body.scrollTop;
if (top == 0) {
scrollStep = 0;
return;
}
if (scrollStep == 0)
scrollStep = top/20 + 1;
top -= scrollStep;
if (top < 0)
top = 0;
document.body.scrollTop = top;
requestAnimationFrame(funScroll);
};
funScroll();
}
window.addEventListener('scroll', function(e) {
var backToTop = document.getElementById('back_to_top')
if (document.body.scrollTop > 0) {
backToTop.style.display = 'block';
} else { backToTop.style.display = 'none' }});
//-->
</script>
<style type="text/css">
#back_to_top {
bottom:20px; right:20px;
width:30px; height:30px;
font-size: 20px;
padding: 2px 5px 2px 5px;
position:fixed;
background-color:rgba(210,210,210,0.1);
border-style: solid;
border-color: rgba(192,192,192,0.8);
border-width:1px;
display:none;
-webkit-transform: rotate(90deg);
-webkit-transform-origin:50% 50%;
}
#back_to_top:focus {
outline-width:0px;
}
</style>
</BODY>

View File

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<userSetting name="C:/Users/fuzzc/Documents/Hardware/pico-ice/pico-ice-video/ice/mandelbrot/promote.xml" version="Diamond (64-bit) 2024.1.0.34.2" date="Thu Sep 26 10:20:19 2024" vendor="Lattice Semiconductor Corporation" >
</userSetting>

View File

@ -0,0 +1,20 @@
module top
(
input wire clk,
input wire start,
output wire [7:0] data
);
reg [7:0] data_r;
always_ff @(posedge clk) begin
data_r <= data_r + 1;
if (data_r < 16 || data_r > 235) begin
data_r <= 16;
end
end
assign data = data_r;
endmodule

8
ice/top.sv Normal file
View File

@ -0,0 +1,8 @@
module top
(
input wire clk,
input wire start,
output wire [7:0] data
);
endmodule

115
main.cpp
View File

@ -1,30 +1,73 @@
#include <ice_fpga.h> #include <ice_fpga.h>
#include <ice_led.h> #include <ice_led.h>
#include <ice_usb.h> #include <ice_usb.h>
#include <tusb.h> #include <tusb.h>
#include <boards/pico_ice.h>
#include <bsp/board_api.h> #include <bsp/board_api.h>
#include <hardware/clocks.h>
#include <pico/stdio.h>
#include <hardware/gpio.h>
#include <hardware/uart.h> #include <hardware/uart.h>
#include <pico/stdio.h>
#include "fpga.pio.h"
static PIO pio;
static uint sm;
#define ICE_FPGA_START_FRAME_PIN ICE_FPGA_14_PIN
static void video_task(); static void video_task();
static void start_next_frame()
{
if (dma_channel_is_busy(DMA_CHANNEL)) { return; }
gpio_put(ICE_FPGA_START_FRAME_PIN, true);
sleep_ms(10);
printf("%d - ", gpio_get(ICE_FPGA_START_FRAME_PIN));
gpio_put(ICE_FPGA_START_FRAME_PIN, false);
sleep_ms(10);
printf("%d - ", gpio_get(ICE_FPGA_START_FRAME_PIN));
pio_sm_set_enabled(pio, sm, false);
pio_sm_clear_fifos(pio, sm);
pio_sm_restart(pio, sm);
dma_channel_set_write_addr(DMA_CHANNEL, frame_buffer[current_frame] - !current_frame, true);
current_frame = !current_frame;
pio_sm_set_enabled(pio, sm, true);
}
int main() int main()
{ {
stdio_init_all(); // set to 120 MHz as it's exactly 2.5x the FPGA clock
set_sys_clock_khz(120000, true);
uart_init(uart0, 115200); tusb_init();
gpio_set_function(0, GPIO_FUNC_UART); stdio_init_all();
gpio_set_function(1, GPIO_FUNC_UART);
ice_led_init(); ice_led_init();
ice_usb_init(); ice_usb_init();
ice_fpga_init(12); ice_fpga_init(48);
ice_fpga_start(); ice_fpga_start();
gpio_init_mask(0b111111111);
gpio_set_dir(ICE_FPGA_27_PIN, GPIO_IN); // 0
gpio_set_dir(ICE_FPGA_25_PIN, GPIO_IN); // 1
gpio_set_dir(ICE_FPGA_21_PIN, GPIO_IN); // 2
gpio_set_dir(ICE_FPGA_19_PIN, GPIO_IN); // 3
gpio_set_dir(ICE_FPGA_26_PIN, GPIO_IN); // 4
gpio_set_dir(ICE_FPGA_23_PIN, GPIO_IN); // 5
gpio_set_dir(ICE_FPGA_20_PIN, GPIO_IN); // 6
gpio_set_dir(ICE_FPGA_18_PIN, GPIO_IN); // 7
gpio_set_dir(ICE_FPGA_START_FRAME_PIN, GPIO_OUT); // 8
gpio_put(ICE_FPGA_START_FRAME_PIN, false);
pio = pio0;
const uint offset = pio_add_program(pio, &fpga_program);
sm = pio_claim_unused_sm(pio, true);
fpga_program_init(pio, sm, offset, 0, 1);
tud_init(0); tud_init(0);
if (board_init_after_tusb) if (board_init_after_tusb)
@ -41,60 +84,16 @@ int main()
} }
static unsigned int interval_ms = 1000 / FRAME_RATE; static unsigned int interval_ms = 1000 / FRAME_RATE;
static unsigned int frame_num = 0;
static bool tx_busy = false; static bool tx_busy = false;
static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8] = { };
static void fill_color_bar(uint8_t* buffer, const unsigned int start_position)
{
static constexpr uint8_t bar_color[8][4] =
{
/* Y, U, Y, V */
{ 235, 128, 235, 128 }, /* 100% White */
{ 219, 16, 219, 138 }, /* Yellow */
{ 188, 154, 188, 16 }, /* Cyan */
{ 173, 42, 173, 26 }, /* Green */
{ 78, 214, 78, 230 }, /* Magenta */
{ 63, 102, 63, 240 }, /* Red */
{ 32, 240, 32, 118 }, /* Blue */
{ 16, 128, 16, 128 }, /* Black */
};
uint8_t* p;
const uint8_t* end = &buffer[FRAME_WIDTH * 2];
const unsigned int index = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2));
p = &buffer[index * 4];
for (unsigned int i = 0; i < 8; i++)
{
for (int j = 0; j < FRAME_WIDTH / (2 * 8); j++)
{
memcpy(p, &bar_color[i], 4);
p += 4;
if (p >= end) { p = buffer; }
}
}
p = &buffer[FRAME_WIDTH * 2];
for (unsigned int i = 1; i < FRAME_HEIGHT; i++)
{
memcpy(p, buffer, FRAME_WIDTH * 2);
p += FRAME_WIDTH * 2;
}
}
void video_task() void video_task()
{ {
static bool already_sent = false; static bool already_sent = false;
static unsigned int start_ms = 0; static unsigned int start_ms = 0;
// don't send data if not streaming
if (!tud_video_n_streaming(0, 0)) if (!tud_video_n_streaming(0, 0))
{ {
already_sent = false; already_sent = false;
frame_num = 0;
return; return;
} }
@ -104,9 +103,8 @@ void video_task()
tx_busy = true; tx_busy = true;
start_ms = board_millis(); start_ms = board_millis();
fill_color_bar(frame_buffer, frame_num); start_next_frame();
tud_video_n_frame_xfer(0, 0, frame_buffer[current_frame], FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
tud_video_n_frame_xfer(0, 0, frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
} }
const unsigned int cur = board_millis(); const unsigned int cur = board_millis();
@ -115,15 +113,14 @@ void video_task()
start_ms += interval_ms; start_ms += interval_ms;
tx_busy = true; tx_busy = true;
fill_color_bar(frame_buffer, frame_num); tud_video_n_frame_xfer(0, 0, frame_buffer[current_frame], FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
tud_video_n_frame_xfer(0, 0, frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
} }
void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
{ {
tx_busy = false; tx_busy = false;
frame_num++; start_next_frame();
} }
int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, const video_probe_and_commit_control_t* parameters) int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, const video_probe_and_commit_control_t* parameters)

View File

@ -69,11 +69,11 @@
// Must be a multiple of flash page size // Must be a multiple of flash page size
#define CFG_TUD_DFU_XFER_BUFSIZE 256 #define CFG_TUD_DFU_XFER_BUFSIZE 256
#define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE 256 #define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE 1023
#define CFG_TUD_VIDEO_STREAMING_BULK 0 #define CFG_TUD_VIDEO_STREAMING_BULK 0
#define FRAME_WIDTH 32 #define FRAME_WIDTH 320
#define FRAME_HEIGHT 18 #define FRAME_HEIGHT 180
#define FRAME_RATE 60 #define FRAME_RATE 60
// Temporarily here until ice_usb.h has necessary info // Temporarily here until ice_usb.h has necessary info