【工程师分享】MPSoC VCU Ctrl-SW 2020.2 输出NV12的YUV文件

FPGA开发圈 2021-05-21 12:00

作者:付汉杰,hankf@xilinx.com,文章转载自: 赛灵思中文社区论坛




01
介绍



Xilinx提供超低延时编解码方案,并提供了全套软件。MPSoC Video Codec Unit提供了详细说明。其中的底层应用软件是VCU Control-Software(Ctrl-SW)。


本文主要说明为Ctrl-SW增加输出NV12视频的功能。


1.1. VCU输入和输出格式

Video Codec Unit(VCU) 输入和输出都是是NV12/NV16格式的视频,Y分量存放在一块连续内存区,UV分量交替存放在Y分量后面的连续内存。具体信息,可以参考VCU Product Guide中的“Source Frame Format”和“Memory Format”。


Ctrl-SW的输入文件最好是NV12/NV16格式的视频文件,由于不需要做格式转换,帧率(FPS)最高。但是Ctrl-SW的输出文件缺省是图像真实分辨率的I420/I422的文件,其中的Y、U、V分量,各自存在一块连续内存,UV分量没有像NV12/NV16格式的视频交替在一起。可以使用FFMPeg等工具,将I420的文件,转换成NV12/NV16格式的文件。


1.2. VCU内存的高度和宽度要求

对于视频的输入内存区,VCU要求高和宽都按32向上对齐。对于1920x1080分辨率,输入的buffer大小至少是1920x1088字节;对于3840x2160分辨率,输入的buffer大小至少是3840x2176字节。


对于视频的输出内存区,VCU要求宽以256地址对齐,高以64地址对齐。对于1920x1080分辨率,输出的buffer大小是2048x1088字节;对于3840x2160分辨率,输出的buffer大小是3840x2176字节。


1.3. VCU内存的pitch

视频数据在内存区中存放时,两行之间的数据可以有间隔。对于每个像素的Y分量用8-bit表示的图像,每个像素的Y分量对应内存的一个字节,图像Y分量的每一行对应的内存大小就是其宽度代表的字节数。比如1920x1080,每一行图像的Y分量需要1920字节内存。如果以2048字节来存储一行1920x1080的图像数据,则在前面存放图像数据,后面的数据被VCU忽略。也可以参考PG252的“Figure 7: Frame Buffer Pitch”。




02
显示YUV文件 2.1. 工具RAW yuv player




Github上的RAW yuv player 能显示YUV文件,它的旧版本Sourceforge RAW yuv player在Sourceforge上。RAW yuv player的菜单“Colour”下,有各种颜色格式,菜单“Size”下有各种分辨率;菜单"Zoom"下可以选择图像缩放比例。


RAW yuv player的YUV420(YV12)格式,就是I420格式,可以显示Ctrl-SW缺省输出的YUV文件。RAW yuv player的NV12格式,也是Ctrl-SW的NV12格式,可以显示修改后的Ctrl-SW输出的YUV NV12文件。


2.1.1. 技巧

各种YUV文件,第一片数据一般都是分量Y。如果发现YUV文件的显示有问题,可以设置好分辨率,在菜单“Colour”下选择“Y”,只看其中的分量Y,当成黑白图片看。如果黑白图片是正常的,说明分量Y是对的。


2.2. hexdump

如果图片内容不对,可以使用二进制比较工具比较错误图片和正确图片,比如Beyond Compare。比较的时候,注意取消对齐设置。


如果没有二进制比较工具,可以使用hexdump把YUV文件按HEX格式转换为文本文件,再用文本比较工具,比如kdiff3、meld进行比较。hexdump输出时,会输出星号"*"代替一样的行;多个重复行,也只输出一个星号。为hexdump加上"-v"选项,则会输出所有数据。

xilinx@XSZHANKF$ hexdump -x test_1080p_h264.264.1f.i420.1920x1080.yuv > test_1080p_h264.264.1f.i420.1920x1080.yuv.hex
xilinx@XSZHANKF$ hexdump -v -x test_1080p_h264.264.1f.i420.1920x1080.yuv > test_1080p_h264.264.1f.i420.1920x1080.yuv.v.hex





03
输出NV12/NV16格式文件




如果Ctrl-SW能输出NV12/NV16格式的文件,Ctrl-SW就能直接对自己的文件进行编码,测试时更加方便。


经过研究,在Ctrl-SW 2020.2里,实现了输出NV12/NV16格式文件的功能。


3.1. 选项

Ctrl-SW里有三种分辨率,分别是图像的真实分辨率,Meta数据分辨率,内存块(buffer)分辨率。


图像的真实分辨率,是真实显示的分辨率。


在Ctrl-SW里,为YUV数据分配内存时,根据图像分辨率,并按对齐要求像是对齐图像分辨率后,得到YUV数据的内存块大小,这就是对应的内存块(buffer)分辨率。对于解码,1920x1080分辨率的内存块是2048x1088字节;3840x2160分辨率的buffer是3840x2176字节。


另外分配内存后,每个内存块有一个对应的Meta数据,保存YUV数据的分辨率。Meta数据分辨率可能比内存块分辨率低。1920x1080分辨率的Meta数据分辨率是2048x1088;而3840x2160分辨率的Meta数据分辨率却是3840x2160,在高度上并没有像1080p时向上对齐。


所以输出NV12/NV16的视频时,也有多种组合。为了测试方便,实现了输出各种组合的NV12/NV16的视频。


如果使用选项“-yuv-nvx”,按得到的Meta数据的分辨率信息,从Y和UV分量的地址,逐行写入到文件。


如果使用选项“-yuv-nvx-1buffer”,行依照pitch长度,高使用分辨率的高度并向上按64字节对齐,计算出YUV整个的内存区大小,相当于内存块(buffer)分辨率,一次性写入到文件。


如果使用选项“-yuv-nvx-stride”,行依照pitch长度,高使用分辨率的高度,从Y和UV分量的地址,逐行输出。相当于宽按内存块(buffer)分辨率,高按Meta数据分辨率输出。


如果使用选项“-yuv-nvx-dispay”,按得到的显示分辨率信息,从Y和UV分量的地址,逐行输出。


另外,还附带增加了输出YUV文件时跳帧、抽帧的功能。如果使用选项“--yuv-skip-num”,则前面的指定数量的帧不会被输出;比如指定5,前面的5帧不会被写入到YUV文件。如果使用选项“--yuv-skip-interval”,则指定数字的倍数序号的帧,才会被输出;比如指定数字3,则只有序号是3的倍数的帧才会被写入到YUV文件。




04
代码




在Ctrl-SW 2020.2里,添加如下代码后,可以直接输出NV12/NV16格式的文件。


下面是增加的全局变量定义。

int gi_yuv_output_skip_frame_num=0;
int gi_yuv_output_skip_frame_interval=0;
int gi_yuv_output_nvx_flag=0;
int gi_yuv_output_nvx_1buffer_flag=0;
int gi_yuv_output_nvx_stride_flag=0;
int gi_yuv_output_nvx_dispay_flag=0;
int gi_yuv_output_dispay_width=0;
int gi_yuv_output_dispay_height=0;

下面是增加的ctrlsw_decoder的命令行选项。

opt.addInt("--yuv-skip-num", &gi_yuv_output_skip_frame_num, "Skip frame number before writing YUV file.");
opt.addInt("--yuv-skip-interval", &gi_yuv_output_skip_frame_interval, "Interval frame number when writing YUV file.");
opt.addFlag("-yuv-nvx", &gi_yuv_output_nvx_flag, "Output NV12/NV16 YUV file.", 1);
opt.addFlag("-yuv-nvx-1buffer", &gi_yuv_output_nvx_1buffer_flag, "Output one continuous NV12/NV16 buffer to YUV file.", 1);
opt.addFlag("-yuv-nvx-stride", &gi_yuv_output_nvx_stride_flag, "Output NV12/NV16 YUV file with VCU round-up padding/stride.", 1);
opt.addFlag("-yuv-nvx-dispay", &gi_yuv_output_nvx_dispay_flag, "Output NV12/NV16 YUV file with display width.", 1);


下面是在UncompressedOutputWriter::ProcessFrame()内部增加的判断是否输出NV12/NV16格式的视频文件的判断代码。

if( gi_yuv_output_skip_frame_num_local <  gi_yuv_output_skip_frame_num )
{
return;
}

if( 0 == (gi_yuv_output_skip_frame_num_local%gi_yuv_output_skip_frame_interval) )
{
return;
}

if( ( 1 == gi_yuv_output_nvx_flag ) || ( 1 == gi_yuv_output_nvx_1buffer_flag )
|| ( 1 == gi_yuv_output_nvx_stride_flag ) || ( 1 == gi_yuv_output_nvx_dispay_flag ) )
{
ProcessFrameNVx( tRecBuf, info, iBdOut);
return;
}



下面是显示视频参数的代码,用于调试,可以被屏蔽掉。

void UncompressedOutputWriter::ShowVideoInfo(AL_TBuffer& tRecBuf, AL_TInfoDecode info, int iBdOut)
{

// only print one time.
static int i_call_time=0;
if( 0 != i_call_time )
{
return;
}
i_call_time++;

iBdOut = convertBitDepthToEven(iBdOut);

auto const iSizePix = (iBdOut + 7) >> 3;

TFourCC tRecFourCC = AL_PixMapBuffer_GetFourCC(&tRecBuf);
printFourCC( tRecFourCC, "Recorded frame buffer", __func__, __LINE__ );

int sx = 1, sy = 1;
AL_GetSubsampling(tRecFourCC, &sx, &sy);

int iPitchSrcY = AL_PixMapBuffer_GetPlanePitch(&tRecBuf, AL_PLANE_Y);

int iPitchSrcUV = AL_PixMapBuffer_GetPlanePitch(&tRecBuf, AL_PLANE_UV);
if( iPitchSrcY != iPitchSrcUV )
{
LogInfo("YUV Y Plane pitch: %d does not equal to UV Plane pitch:%d at %s:%d.\n",
iPitchSrcY, iPitchSrcUV, __func__, __LINE__ );
}
LogVerbose("YUV Y Plane pitch:%d, UV Plane pitch:%d at %s:%d.\n", iPitchSrcY, iPitchSrcUV, __func__, __LINE__ );

AL_TDimension tYuvDim = AL_PixMapBuffer_GetDimension(&tRecBuf);
int const iRoundUpYWidth = RoundUp(tYuvDim.iWidth, 256);
if( iPitchSrcY != iRoundUpYWidth )
{
LogInfo("YUV Y Plane pitch %d does not equal to Y Round-up Width:%d at %s:%d.\n",
iPitchSrcY, iRoundUpYWidth, __func__, __LINE__ );
}

// For 1920x1080, YuvDim.iHeight is always rounded up, it is 1920x1088.
// For 3840x2160, YuvDim.iHeight is not rounded up, it is 3840x2160.
int const iRoundUpHeight = RoundUp(tYuvDim.iHeight, 64);
if( tYuvDim.iHeight != iRoundUpHeight )
{
// For 4K video, tYuvDim.iHeight(2160) does not equal to Round-up Height(2176)
LogInfo("YUV Height: %d does not equal to Round-up Height:%d at %s:%d.\n",
tYuvDim.iHeight, iRoundUpHeight, __func__, __LINE__ );
}

//int const iNumPix = tYuvDim.iHeight * tYuvDim.iWidth; // For I420 without extra padding bytes.
const AL_EChromaMode stRecChromaMode = AL_GetChromaMode(tRecFourCC);
//int const iNumPixC = AL_GetChromaMode(tRecFourCC) == AL_CHROMA_MONO ? 0 : ((tYuvDim.iWidth + sx - 1) / sx) * ((tYuvDim.iHeight + sy - 1) / sy);
int const iLineNumPixC = (stRecChromaMode == AL_CHROMA_MONO) ? 0 : ((tYuvDim.iWidth + sx - 1) / sx) ;
int const iRoundUpLineNumPixC = (stRecChromaMode == AL_CHROMA_MONO) ? 0 : ((iPitchSrcUV + sx - 1) / sx) ;

// Get display in sResolutionFound()
int const iNumPix = tYuvDim.iHeight * tYuvDim.iWidth;
int const iRoundUpNumPix = iRoundUpHeight * iPitchSrcY;

int const iNumPixC = iLineNumPixC * ((tYuvDim.iHeight + sy - 1) / sy);
int const iRoundUpNumPixC = iRoundUpLineNumPixC * ((iRoundUpHeight + sy - 1) / sy);
int i_y_buffer_size = iRoundUpNumPix * iSizePix;
int i_uv_buffer_size = 2 * iRoundUpNumPixC * iSizePix;

int i_yuv_buffer_size = i_y_buffer_size + i_uv_buffer_size;
LogVerbose("Subsampling sx:%d, sy:%d at %s:%d.\n", sx, sy, __func__, __LINE__ );
LogVerbose("Height:%d, Width:%d at %s:%d.\n", tYuvDim.iHeight, tYuvDim.iWidth, __func__, __LINE__ );
LogVerbose("Roundup Height:%d, Width:%d at %s:%d.\n", iRoundUpHeight, iPitchSrcY, __func__, __LINE__ );
LogVerbose("iNumPix:%d, iLineNumPixC:%d, iNumPixC:%d, iSizePix:%d at %s:%d.\n", iNumPix, iLineNumPixC, iNumPixC, iSizePix, __func__, __LINE__ );
LogVerbose("iRoundUpNumPix:%d, iRoundUpLineNumPixC:%d, iRoundUpNumPixC:%d at %s:%d.\n", iRoundUpNumPix, iRoundUpLineNumPixC, iRoundUpNumPixC, __func__, __LINE__ );

LogVerbose("NV12/NV16 YUV Plane Y: %d, UV: %d YUV: %d bytes at %s:%d.\n",
i_y_buffer_size, i_uv_buffer_size, i_yuv_buffer_size, __func__, __LINE__ );

uint8_t* p_buff_y_plane = AL_PixMapBuffer_GetPlaneAddress(&tRecBuf, AL_PLANE_Y);
uint8_t* p_buff_uv_plane = AL_PixMapBuffer_GetPlaneAddress(&tRecBuf, AL_PLANE_UV);
LogVerbose("NV12/NV16 YUV Y Plane address: %p, UV Plane address: %p at %s:%d.\n",
p_buff_y_plane, p_buff_uv_plane, __func__, __LINE__ );

int const iOffsetY_UV_Plane = ( (unsigned long long)p_buff_uv_plane - (unsigned long long)p_buff_y_plane);
if( i_y_buffer_size != iOffsetY_UV_Plane )
{
LogInfo("YUV Y Plane sieze: %d does not equal to offset: %d between Y/UV plane at %s:%d.\n",
i_y_buffer_size, iOffsetY_UV_Plane, __func__, __LINE__ );
}

}


下面是增加的输出NV12/NV16格式的视频文件的主体代码。

void UncompressedOutputWriter::ProcessFrameNVx(AL_TBuffer& tRecBuf, AL_TInfoDecode info, int iBdOut)
{

if(!(YuvFile.is_open() || CertCrcFile.is_open()))
return;

static int i_call_time=0;
i_call_time++;

iBdOut = convertBitDepthToEven(iBdOut);

auto const iSizePix = (iBdOut + 7) >> 3;

TFourCC tRecFourCC = AL_PixMapBuffer_GetFourCC(&tRecBuf);

#if 1
ShowVideoInfo( tRecBuf, info, iBdOut);
#endif

int sx = 1, sy = 1;
AL_GetSubsampling(tRecFourCC, &sx, &sy);

int iPitchSrcY = AL_PixMapBuffer_GetPlanePitch(&tRecBuf, AL_PLANE_Y);
int iPitchSrcUV = AL_PixMapBuffer_GetPlanePitch(&tRecBuf, AL_PLANE_UV);

AL_TDimension tYuvDim = AL_PixMapBuffer_GetDimension(&tRecBuf);

const AL_EChromaMode stRecChromaMode = AL_GetChromaMode(tRecFourCC);
int const iLineNumPixC = (stRecChromaMode == AL_CHROMA_MONO) ? 0 : ((tYuvDim.iWidth + sx - 1) / sx) ;

uint8_t* p_buff_y_plane = AL_PixMapBuffer_GetPlaneAddress(&tRecBuf, AL_PLANE_Y);
LogVerbose("NV12/NV16 YUV data Y Plane address: %p at %s:%d.\n", p_buff_y_plane, __func__, __LINE__ );

LogVerbose("YUV data with VCU padding Height:%d, Y Width:%d, UV Width:%d at %s:%d.\n",
tYuvDim.iHeight, iPitchSrcY, iPitchSrcUV, __func__, __LINE__ );

if( 1 == gi_yuv_output_nvx_1buffer_flag )
{

// For 1920x1080, YuvDim.iHeight is always rounded up, it is 1920x1088.
// For 3840x2160, YuvDim.iHeight is not rounded up, it is 3840x2160.
int const iWriteHeight = RoundUp(tYuvDim.iHeight, 64);

//int const iNumPix = tYuvDim.iHeight * tYuvDim.iWidth; // For I420 without extra padding bytes.
int const iRoundUpNumPix = iWriteHeight * iPitchSrcY; // For NV12/NV16 with extra padding bytes.
//int const iNumPixC = AL_GetChromaMode(tRecFourCC) == AL_CHROMA_MONO ? 0 : ((tYuvDim.iWidth + sx - 1) / sx) * ((tYuvDim.iHeight + sy - 1) / sy);
int const iRoundUpLineNumPixC = (stRecChromaMode == AL_CHROMA_MONO) ? 0 : ((iPitchSrcUV + sx - 1) / sx) ;
int const iRoundUpNumPixC = iRoundUpLineNumPixC * ((iWriteHeight + sy - 1) / sy);
int i_y_buffer_size = iRoundUpNumPix * iSizePix;
int i_uv_buffer_size = 2 * iRoundUpNumPixC * iSizePix;
int i_yuv_buffer_size = i_y_buffer_size + i_uv_buffer_size;

// Display YUV file: 1920x1080: 2048x1088; 3840x2160: 3840 x 2176
if( 1 == i_call_time )
{
LogInfo("Diplay NV12/NV16 YUV file of one continuous buffer with Height:%d, Y Width:%d, UV Width:%d at %s:%d.\n",
iWriteHeight, iPitchSrcY, iPitchSrcUV, __func__, __LINE__ );
}
YuvFile.write((const char*)p_buff_y_plane, i_yuv_buffer_size);
}
else
{
int iWriteHeight;
int iWriteYWidth;
int iWriteUVWidth;

// For 1920x1080, YuvDim.iHeight is always rounded up, it is 1920x1088.
// For 3840x2160, YuvDim.iHeight is not rounded up, it is 3840x2160.
if( 1 == gi_yuv_output_nvx_stride_flag )
{
iWriteHeight = tYuvDim.iHeight;
iWriteYWidth = iPitchSrcY;
iWriteUVWidth = 2 * ((iPitchSrcUV + sx - 1) / sx);
LogVerbose("Diplay NV12/NV16 YUV file of stride with Height:%d, Y Width:%d, UV Width:%d at %s:%d.\n",
tYuvDim.iHeight, iPitchSrcY, iPitchSrcUV, __func__, __LINE__ );
}
else
{
if( 0 != gi_yuv_output_dispay_height )
{
// Display YUV file: 1920x1080: 1920x1080.
iWriteHeight = gi_yuv_output_dispay_height;
}
else
{
// Display YUV file: 1920x1080: 1920x1088.
iWriteHeight =tYuvDim.iHeight;
}

if( 0 != gi_yuv_output_dispay_width )
{
iWriteYWidth = gi_yuv_output_dispay_width;
iWriteUVWidth = 2 * ((gi_yuv_output_dispay_width + sx - 1) / sx);
}
else
{
iWriteYWidth =tYuvDim.iWidth;
iWriteUVWidth = 2 * iLineNumPixC;
}

}
if( 1 == i_call_time )
{
LogInfo("Diplay NV12/NV16 YUV file with Height: %d, Y Width: %d, UV Width: %d at %s:%d.\n",
iWriteHeight, iWriteYWidth, iWriteUVWidth, __func__, __LINE__ );
}

// two writes, skipp padding bytes between Y plane and UV plane.
uint8_t* p_buff_y=p_buff_y_plane;
int iYBytes=0;
for( int i=0; i<iWriteHeight; i++ )
{
// Write each line of Y plane, and skipp padding bytes between each line.
YuvFile.write((const char*)p_buff_y, iWriteYWidth);
p_buff_y = p_buff_y + iPitchSrcY;
iYBytes+=iWriteYWidth;
}

uint8_t* p_buff_uv_plane = AL_PixMapBuffer_GetPlaneAddress(&tRecBuf, AL_PLANE_UV);
LogVerbose("NV12/NV16 YUV data UV Plane address: %p at %s:%d.\n", p_buff_uv_plane, __func__, __LINE__ );
uint8_t* p_buff_uv=p_buff_uv_plane;
int iUVBytes=0;
for( int i=0; i<(iWriteHeight/sy); i++ )
{
// Write each line of UV plane, and skipp padding bytes between each line.
YuvFile.write((const char*)p_buff_uv, iWriteUVWidth);
p_buff_uv = p_buff_uv + iPitchSrcUV;
iUVBytes+=iWriteUVWidth;
}
LogVerbose("write NV12/NV16 YUV data Y: %d bytes, UV: %d bytes at %s:%d.\n",
iYBytes, iUVBytes, __func__, __LINE__ );
}
}





05
5. 测试




在开发过程中,测试了1920x1080、3840x2160分辨率的NV12图像。


5.1. 1080分辨率

1920x1080分辨率时,以内存块分辨率输出,分辨率是1920x1088;以pitch长度和分辨率高度向上对齐后输出,分辨率是2048x1088;以pitch长度和Meta分辨率的高度输出,分辨率是2048x1088;以显示分辨率输出,分辨率是1920x1080。如果没有特殊说明,图像都是NV12格式的,也是以NV12格式显示。


查看YUV文件时,必须设置正确的分辨率和格式,否则数据显示会混乱。


以分辨率1920x1080显示选项“-yuv-nvx-dispay”输出的图片,结果正常。


以分辨率2048x1088显示选项“-yuv-nvx-1buffer”输出的图片,结果正常。右边有一块红色图像,是因为对应的内存区没有真实图像数据。


以分辨率2048x1088显示选项“-yuv-nvx-stride”输出的图片,结果正常。右边也有一块红色图像。


5.1.1. 分辨率显示格式错误的现象

以分辨率1920x1080显示分辨率2048x1088图片,图像混乱了。因为实际图像数据每行2048字节,显示时每行1920字节,所以读出的数据混乱了,上面的连续红色块变成了小块,分布到了图像各处。


以分辨率2048x1080显示分辨率2048x1088图片,最上面有绿条,因为把8行的Y分量数据当成了UV分量数据。这8行的实际图像是黑色的。


5.2. 3840x2160分辨率

3840x2160分辨率时,以pitch长度和分辨率高度向上对齐后的分辨率输出,分辨率是3840x2176;其它模式输出,分辨率都是3840x2160。


以分辨率3840x2160显示选项“-yuv-nvx-dispay”输出的图片,结果正常。


以分辨率3840x2176显示选项“-yuv-nvx-1buffer”输出的图片,结果正常。下面有一条绿色,也是因为对应的内存区没有有效的图像数据。


5.2.1. x2160分辨率显示格式错误的现象

以分辨率3840x2176格式显示分辨率3840x2160的图片,最下面有绿条,是因为把部分UV分量数据当成了Y分量数据,导致最下面部分图像缺少UV分量数据。


以分辨率3840x2160、I420格式显示选项“-yuv-nvx-stride”输出的NV12分辨率3840x2176图片,轮廓正常,色彩异常。轮廓正常,是因为对Y分量数据的解析是对的;色彩异常是因为对UV分量数据的解析是错的。




06
未来工作




未来可以继续测试NV16的图像,也可以测试其它分辨率的图像。




07
参考文档




XilinxPG252 (v2020.2) H.264/H.265 Video Codec Unit v1.2 Solution


》》》点击“阅读原文”下载参考文档


关注我们

FPGA开发圈 这里介绍、交流、有关FPGA开发资料(文档下载,技术解答等),提升FPGA应用能力。
评论
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 73浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 100浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 41浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 390浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 121浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 164浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 183浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 150浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 111浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 49浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 182浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦