相关文章

1.《【Audio】I2S传输PCM音频数据分析总结(一)》
2.《【Audio】I2S传输PCM音频数据分析总结(二)》
3.《【Audio】基于STM32 I2S移植WM8978 Audio Codec驱动》

1. WM8978简介

WM8978是一款低功耗,高质量的立体声编解码器,专为便携式应用,如数码相机或数码摄像机等。

该芯片集成了立体声差分麦克风的前置放大器,并包括扬声器、耳机和差分或立体声线输出的驱动器。外部组件要求减少,因为不需要单独的麦克风或耳机放大器。

WM8978的功能框图如下所示:

2. WM8978硬件连接

使用STM32F429+WM8978硬件平台,通过I2S接口来读写音频数据,I2C接口发送写命令控制WM8978相关功能。

STM32F429与WM8978的引脚连接如下:

STM32引脚名称 WM8978引脚名称 功能 描述
GPIOB12 LRC I2S WS 字选择,是音频数据控制信号输出,0:左声道的数据,1:右声道的数据
GPIOD3 BCLK I2S BCLK 串行时钟,也叫位时钟,对应数字音频的每一位数据。
GPIOC2 ADCDAT I2S EXT_SD 控制 I2S 全双工模式的附加串行数据引脚,用于接收音频数据。
GPIOI3 DACDAT I2S SD 串行数据,用于发送音频数据。
GPIOC6 MCLK I2S MCLK 当 I2S 配置为主模式时,使用主时钟(单独映射)输出此附加时钟。

(备注:I2C不是本篇文章的重点,这里会忽略对它的介绍。重点:WM8978的I2C只能写,不能读。)

3. STM32 I2S的配置

STM32 I2S的配置主要是:

  1. I2S相关GPIO的初始化
  2. I2S相关寄存器的初始化
  3. I2S TX和RX的DMA的初始化
  • I2S相关GPIO的初始化
    STM32的I2S和SPI是公用的pin脚,所以这里需要将IO设置为I2S模式。这些I2S的引脚的相关功能在上面的表格中有详细描述,下面是具体初始化的代码:
/*** I2S总线传输音频数据口线* WM8978_LRC    -> PB12/I2S2_WS* WM8978_BCLK   -> PD3/I2S2_CK* WM8978_ADCDAT -> PC2/I2S2ext_SD* WM8978_DACDAT -> PI3/I2S2_SD* WM8978_MCLK   -> PC6/I2S2_MCK*/
static void I2S_Gpio_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;/* Enable GPIO clock */RCC_AHB1PeriphClockCmd(I2S_WS_GPIO_CLK|I2S_BCLK_GPIO_CLK| \I2S_ADCDAT_GPIO_CLK|I2S_DACDAT_GPIO_CLK| \I2S_MCLK_GPIO_CLK, ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Pin = I2S_WS_PIN;GPIO_Init(I2S_WS_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = I2S_BCLK_PIN;GPIO_Init(I2S_BCLK_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = I2S_ADCDAT_PIN;GPIO_Init(I2S_ADCDAT_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = I2S_DACDAT_PIN;GPIO_Init(I2S_DACDAT_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = I2S_MCLK_PIN;GPIO_Init(I2S_MCLK_PORT, &GPIO_InitStructure);/* Connect pins to I2S peripheral  */GPIO_PinAFConfig(I2S_WS_PORT,     I2S_WS_SOURCE,     I2S_WS_AF);GPIO_PinAFConfig(I2S_BCLK_PORT,   I2S_BCLK_SOURCE,   I2S_BCLK_AF);GPIO_PinAFConfig(I2S_ADCDAT_PORT, I2S_ADCDAT_SOURCE, I2S_ADCDAT_AF);GPIO_PinAFConfig(I2S_DACDAT_PORT, I2S_DACDAT_SOURCE, I2S_DACDAT_AF);GPIO_PinAFConfig(I2S_MCLK_PORT,   I2S_MCLK_SOURCE,   I2S_MCLK_AF);
}
  • I2S相关寄存器的初始化
    主要是设置:

    • I2S_AudioFreq = I2S_AudioFreq_44k //音频数据的采样率为44.1KHz
    • I2S_DataFormat = I2S_DataFormat_16b // 音频数据的数据宽度为16bit
    • I2S_Standard = I2S_Standard_Phillips // I2S传输音频数据采用Phillips I2S的标准

    具体配置代码如下:

void I2S_Mode_Config(const uint16_t _usStandard,const uint16_t _usWordLen,const uint32_t _usAudioFreq)
{I2S_InitTypeDef I2S_InitStructure;uint32_t n = 0;FlagStatus status = RESET;/***   For I2S mode, make sure that either:*    - I2S PLL is configured using the functions RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S),*      RCC_PLLI2SCmd(ENABLE) and RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY).*/RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S);RCC_PLLI2SCmd(ENABLE);for (n = 0; n < 500; n++){status = RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY);if (status == 1)break;}/* Enable the CODEC_I2S peripheral clock */RCC_APB1PeriphClockCmd(I2S2_CLK, ENABLE);/* CODEC_I2S peripheral configuration */SPI_I2S_DeInit(I2S2_SPI);I2S_InitStructure.I2S_AudioFreq = _usAudioFreq;I2S_InitStructure.I2S_Standard = _usStandard;I2S_InitStructure.I2S_DataFormat = _usWordLen;I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;/* Initialize the I2S peripheral with the structure above */I2S_Init(I2S2_SPI, &I2S_InitStructure);I2S_Cmd(I2S2_SPI, ENABLE);/* Configures the full duplex mode for the I2S2 */I2S_FullDuplexConfig(I2S2_ext, &I2S_InitStructure);I2S_Cmd(I2S2_ext, ENABLE);
}
  • I2S TX和RX的DMA的初始化
    WM8978 Audio Codec驱动需要实现2个功能:播放和录音,所以这里将会很多的数据需要发送和接收。为了减轻CPU的负担,这里需要使用I2S的TX和RX的DMA。

    ???为什么选择的是DMA1 Stream4 Channel0和Stream3 Channel3???
    下面的截图是DMA的功能框图,我们需要使用它将I2S外设数据接收到内存,将内存的数据发送到I2S外设。我们需要根据硬件的外设来选择对应DMA的stream和channel。

    根据硬件连接,使用的是SPI2硬件接口复用的I2S2。由于使用了I2S全双工功能,并且通过I2S2_EXT来接收数据,所以这里选择DMA1的I2S2_EXT_RX接收数据。I2S2_SD_TX引脚复用了SPI2_TX,所以这里选择SPI2_TX发送数据。到这里就解释了为什么选择的是DMA1 Stream4 Channel0和Stream3 Channel3?下面是SMT32 DMA1映射表:

    下面是TX DMA的初始化的例子,RX DMA初始化是同样的过程。主要是步骤如下:

    • 使能DMA的时钟
    • 指定外设和内存的数据存放地址
    • 配置DMA的相关属性参数
    • 设置DMA中断参数,通过中断来指示数据是否传送完成。
void I2Sx_TX_DMA_Init(const uint16_t *dmaM0Addr,const uint16_t *dmaM1Addr,const uint32_t num)
{NVIC_InitTypeDef   NVIC_InitStructure;DMA_InitTypeDef  DMA_InitStructure;/* Enable the DMA clock */RCC_AHB1PeriphClockCmd(I2Sx_DMA_CLK, ENABLE); /* Configure the DMA Stream */DMA_DeInit(I2Sx_TX_DMA_STREAM);while (DMA_GetCmdStatus(I2Sx_TX_DMA_STREAM) != DISABLE){}//等待DMA1_Stream4可配置 DMA_ClearITPendingBit(I2Sx_TX_DMA_STREAM,DMA_IT_FEIF4|DMA_IT_DMEIF4|DMA_IT_TEIF4|DMA_IT_HTIF4|DMA_IT_TCIF4);//清空DMA1_Stream4上所有中断标志/* 配置 DMA Stream */DMA_InitStructure.DMA_Channel = I2Sx_TX_DMA_CHANNEL;  //通道0 SPIx_TX通道 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2S2_SPI->DR;//外设地址为:(u32)&SPI2->DRDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)dmaM0Addr;//DMA 存储器0地址DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式DMA_InitStructure.DMA_BufferSize = num;//数据传输量 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据长度:16位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器数据长度:16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不使用FIFO模式        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输DMA_Init(I2Sx_TX_DMA_STREAM, &DMA_InitStructure);//初始化DMA StreamDMA_DoubleBufferModeConfig(I2Sx_TX_DMA_STREAM, (uint32_t)dmaM0Addr, DMA_Memory_0);//双缓冲模式配置DMA_DoubleBufferModeConfig(I2Sx_TX_DMA_STREAM, (uint32_t)dmaM1Addr, DMA_Memory_1);//双缓冲模式配置DMA_DoubleBufferModeCmd(I2Sx_TX_DMA_STREAM, ENABLE);//双缓冲模式开启DMA_ITConfig(I2Sx_TX_DMA_STREAM,DMA_IT_TC,ENABLE);//开启传输完成中断SPI_I2S_DMACmd(I2S2_SPI,SPI_I2S_DMAReq_Tx,ENABLE);//SPI2 TX DMA请求使能.NVIC_InitStructure.NVIC_IRQChannel = I2Sx_TX_DMA_STREAM_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子优先级2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道NVIC_Init(&NVIC_InitStructure);//配置
}

4. WM8978 Audio Codec的配置

下面介绍关于WM8978主要的寄存器的设置:

  • wm8978_SetOUT1Volume()
  • wm8978_SetMicGain()
  • wm8978_SetLineGain()
  • wm8978_CfgAudioIF()
  • wm8978_CfgAudioPath()

4.1 wm8978_SetOUT1Volume()

wm8978_SetOUT1Volume()函数主要是修改输出通道1音量,具体设置WM8978的功能框图如下:

具体设置代码如下:

/*** @brief  修改输出通道1音量* @param  _ucVolume :音量值, 0-63* @retval 无*/
void wm8978_SetOUT1Volume(uint8_t _ucVolume)
{uint16_t regL;uint16_t regR;if (_ucVolume > VOLUME_MAX){_ucVolume = VOLUME_MAX;}regL = _ucVolume;regR = _ucVolume;/*R52  LOUT1 Volume controlR53 ROUT1 Volume control*//* 先更新左声道缓存值 */wm8978_WriteReg(WM8978_LOUT1_HP_CONTROL, regL | 0x00);/* 再同步更新左右声道的音量 */wm8978_WriteReg(WM8978_ROUT1_HP_CONTROL, regR | 0x100);    /* 0x180表示 在音量为0时再更新,避免调节音量出现的“嘎哒”声 */
}

4.2 wm8978_SetMicGain()

wm8978_SetMicGain()函数主要是设置MIC的增益,具体设置WM8978的功能框图如下:

具体设置代码如下:

/*** @brief  设置增益* @param  _ucGain :增益值, 0-63* @retval 无*/
void wm8978_SetMicGain(uint8_t _ucGain)
{if (_ucGain > GAIN_MAX){_ucGain = GAIN_MAX;}/* PGA 音量控制  R45, R46 Bit8  INPPGAUPDATEBit7    INPPGAZCL       过零再更改Bit6   INPPGAMUTEL     PGA静音Bit5:0 增益值,010000是0dB*/wm8978_WriteReg(WM8978_LEFT_INP_PGA_CONTROL,  _ucGain);wm8978_WriteReg(WM8978_RIGHT_INP_PGA_CONTROL, _ucGain | (1 << 8));
}

4.3 wm8978_SetLineGain()

wm8978_SetLineGain()函数主要是设置输入通道的增益,具体设置WM8978的功能框图如下:

具体设置代码如下:

/*** @brief  设置Line输入通道的增益* @param  _ucGain :音量值, 0-7. 7最大,0最小。 可衰减可放大。* @retval 无*/
void wm8978_SetLineGain(uint8_t _ucGain)
{uint16_t usRegValue;if (_ucGain > 7){_ucGain = 7;}/*Mic 输入信道的增益由 PGABOOSTL 和 PGABOOSTR 控制Aux 输入信道的输入增益由 AUXL2BOOSTVO[2:0] 和 AUXR2BOOSTVO[2:0] 控制Line 输入信道的增益由 LIP2BOOSTVOL[2:0] 和 RIP2BOOSTVOL[2:0] 控制*//* R47(左声道),R48(右声道), MIC 增益控制寄存器R47 (R48定义与此相同)B8      PGABOOSTL   = 1,   0表示MIC信号直通无增益,1表示MIC信号+20dB增益(通过自举电路)B7       = 0, 保留B6:4 L2_2BOOSTVOL = x, 0表示禁止,1-7表示增益-12dB ~ +6dB  (可以衰减也可以放大)B3        = 0, 保留B2:0`   AUXL2BOOSTVOL = x,0表示禁止,1-7表示增益-12dB ~ +6dB  (可以衰减也可以放大)*/usRegValue = wm8978_ReadReg(WM8978_LEFT_ADC_BOOST_CONTROL);usRegValue &= 0x8F;/* 将Bit6:4清0   1000 1111*/usRegValue |= (_ucGain << 4);wm8978_WriteReg(WM8978_LEFT_ADC_BOOST_CONTROL, usRegValue);   /* 写左声道输入增益控制寄存器 */usRegValue = wm8978_ReadReg(WM8978_RIGHT_ADC_BOOST_CONTROL);usRegValue &= 0x8F;/* 将Bit6:4清0   1000 1111*/usRegValue |= (_ucGain << 4);wm8978_WriteReg(WM8978_RIGHT_ADC_BOOST_CONTROL, usRegValue);  /* 写右声道输入增益控制寄存器 */
}

4.4 wm8978_CfgAudioIF()

wm8978_CfgAudioIF()函数主要是配置WM8978的I2S接口和时钟,具体设置WM8978的功能框图如下:

具体设置代码如下:

/*** @brief  配置WM8978的音频接口(I2S)* @param  _usStandard : 接口标准,I2S_Standard_Phillips, I2S_Standard_MSB 或 I2S_Standard_LSB* @param  _ucWordLen : 字长,16、24、32  (丢弃不常用的20bit格式)* @retval 无*/
void wm8978_CfgAudioIF(uint16_t _usStandard, uint8_t _ucWordLen)
{uint16_t usReg;/* WM8978(V4.5_2011).pdf 73页,寄存器列表 *//*  REG R4, 音频接口控制寄存器B8     BCP  = X, BCLK极性,0表示正常,1表示反相B7       LRCP = x, LRC时钟极性,0表示正常,1表示反相B6:5    WL = x, 字长,00=16bit,01=20bit,10=24bit,11=32bit (右对齐模式只能操作在最大24bit)B4:3   FMT = x,音频数据格式,00=右对齐,01=左对齐,10=I2S格式,11=PCMB2      DACLRSWAP = x, 控制DAC数据出现在LRC时钟的左边还是右边B1        ADCLRSWAP = x,控制ADC数据出现在LRC时钟的左边还是右边B0      MONO    = 0,0表示立体声,1表示单声道,仅左声道有效*/usReg = 0;if (_usStandard == I2S_Standard_Phillips)  /* I2S飞利浦标准 */{usReg |= WM8978_R4_FMT_I2S_FORMAT;}else if (_usStandard == I2S_Standard_MSB)  /* MSB对齐标准(左对齐) */{usReg |= WM8978_R4_FMT_LEFT_JUSTIFIED;}else if (_usStandard == I2S_Standard_LSB)  /* LSB对齐标准(右对齐) */{usReg |= WM8978_R4_FMT_RIGHT_JUSTIFIED;}else    /* PCM标准(16位通道帧上带长或短帧同步或者16位数据帧扩展为32位通道帧) */{usReg |= WM8978_R4_FMT_PCM_MODE;}if (_ucWordLen == 24){usReg |= WM8978_R4_WORD_LEN_24_BITS;}else if (_ucWordLen == 32){usReg |= WM8978_R4_WORD_LEN_32_BITS;}else{usReg |= WM8978_R4_WORD_LEN_16_BITS;      /* 16bit */}wm8978_WriteReg(WM8978_AUDIO_INTERFACE, usReg);/*R6,时钟产生控制寄存器MS = 0,  WM8978被动时钟,由MCU提供MCLK时钟*/wm8978_WriteReg(WM8978_CLOCKING, 0x000);
}

4.5 wm8978_CfgAudioPath()

wm8978_CfgAudioPath()函数主要是配置WM8978的音频通道,我这里demo实现的功能是MIC录音耳机输出,具体设置WM8978的功能框图如下:

配置音频通道涉及到很多寄存器,这里就不一一列举,需要查看的可以下载完成的代码来分析(备注:文章最后会列出Demo工程的下载路径)。具体涉及到的代码如下:

/*** @brief  配置wm8978音频通道* @param  _InPath : 音频输入通道配置* @param  _OutPath : 音频输出通道配置* @retval 无*/
void wm8978_CfgAudioPath(uint16_t _InPath, uint16_t _OutPath)
{/* 查看WM8978数据手册的 REGISTER MAP 章节, 第89页 */if ((_InPath == IN_PATH_OFF) && (_OutPath == OUT_PATH_OFF)){wm8978_PowerDown();return;}wm8978_Set_R1_Power_Manage_1(_InPath, _OutPath);wm8978_Set_R2_Power_Manage_2(_InPath, _OutPath);wm8978_Set_R3_Power_Manage_3(_InPath, _OutPath);wm8978_Set_R14_ADC_Ctrl(_InPath);wm8978_Set_R27_30_Notch_Filter(_InPath);wm8978_Set_R32_35_ALC_Ctrl();wm8978_Set_R47_48_Input_Boost_Ctrl(_InPath);wm8978_Set_R15_16_ADC_Digital_Vol();wm8978_Set_R43_Beep_Ctrl(_InPath, _OutPath);wm8978_Set_R49_Output_Ctrl(_InPath, _OutPath);wm8978_Set_R50_51_Output_Mixer_Ctrl(_InPath);wm8978_Set_R56_OUT3_Mixer_Ctrl(_OutPath);wm8978_Set_R57_OUT4_Mixer_Ctrl(_OutPath);wm8978_Set_R11_12_DAC_Digital_Vol(_InPath);wm8978_Set_R10_DAC_Ctrl(_InPath);
}

5. 验证测试

运行WM8978 Demo可以正常的录音和播放,测试成功:

下面是通过WM8978 Demo播放采样率44.1KHz 16bit双声道正弦波1KHz的PCM音频数据时,用逻辑分析仪抓取的I2S数据图如下:

6. 资料下载

移植成功的完整工程代码下载路径如下:
https://download.csdn.net/download/ZHONGCAI0901/18375355

【Audio】基于STM32 I2S移植WM8978 Audio Codec驱动相关推荐

  1. STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片

    STM32CubeMX | 基于STM32使用HAL库硬件SPI驱动WK2124一拖四SPI转四路串口芯片 STM32基础工程生成 首先使用STM32CUBEMX生成STM32的基础工程,配置时钟到7 ...

  2. 基于STM32单片机的ILI9341液晶屏驱动protues仿真

    硬件设计 (末尾附文件) 代码设计 #include "lcd.h" #include "string.h" #include "font.h&quo ...

  3. 【随笔记】XR872 Codec 驱动移植和应用程序实例(附芯片调试方法)

    XR872 的 SDK 是我目前接触过那么多款 MCU 的 SDK 中,唯一一个将框架和 RTOS 结合的非常完美的 SDK .无论是代码风格还是框架的设计,看起来都很赏心悦目,而且是源码开源.希望能 ...

  4. HIH8121(HIH8000系列)温湿度传感器驱动代码-基于STM32 HAL库

    HIH8121温湿度传感器代码基于STM32 HAL库 HIH8121传感器简介 驱动代码.c 驱动代码.h main函数 HIH8121传感器简介 HIH8121是霍尼韦尔公司生产的测量温湿度参数一 ...

  5. 基于STM32的VEML6040颜色传感器应用

    *以下文章资源均来自互联网,文章内容仅供参考,如果侵犯了您的权益,请留言或通过邮箱与我联系. The following article resources are all from the Inte ...

  6. 高通Audio中ASOC的codec驱动(二)

    继上一篇文章:高通Audio中ASOC的machine驱动(一) ASOC的出现是为了让codec独立于CPU,减少和CPU之间的耦合,这样同一个codec驱动就无需修改就可以匹配任何一款平台. 在M ...

  7. 基于STM32标准库的MS5837程序移植

    基于STM32标准库的MS5837程序移植 一.准备工作 1. 硬件电路 2. 新建工程 二.开始移植 1. IIC底层模拟 2. MS5837移植 3. 主函数编写 4. 代码调试结果 三.源代码下 ...

  8. php项目网页音乐播放器插件,基于HTML5 canvas和Web Audio的音频播放器插件

    wavesurfer.js是一款基于HTML5 canvas和Web Audio的音频播放器插件.通过wavesurfer.js你可以使用它来制作各种HTML5音频播放器,它可以在各种支持 Web A ...

  9. 【FatFs】基于STM32 SD卡移植FatFs文件系统

    相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...

  10. 再谈关于我原来写的一篇博文《终于成功安装了 SigmaTel High Definition Audio CODEC 驱动》

    我许久之前写过一篇博文<终于成功安装了 SigmaTel High Definition Audio CODEC 驱动>.在写出来之后,许多朋友看过,或许真的帮有的朋友解决了问题,但是发表 ...

最新文章

  1. 排序与查找实验报告java,查找排序实验报告 - 范文大全 - 作文仓库-www.zuowencangku.com...
  2. FPGA从Xilinx的7系列学起(7)
  3. 判断三角形与射线相交的完整代码。。。
  4. Java基础:Java变量、数据类型、运算符(2)
  5. 科大星云诗社动态20210910
  6. Runtime Error VS Accepted (大整数排序 )
  7. 腾讯朱华:数据中心下一个风向的探索
  8. java关于hashmap编程题_LeetCode算法题-Design HashMap(Java实现)
  9. 做了这么多项目才知道「模态弹窗」是这么用的
  10. .Net控件Telerik全套下载:Telerik Controls 2010 Q2 (附加DLL文件+源码)
  11. android markdown编辑器,推荐几款好用的Markdown编辑器
  12. linux怎么查看tudexo状态,linux怎么安装tuxedo中间件?
  13. ASCII码二进制对照表及其规律
  14. 【考研经验】双非二战山东大学计算机技术初试第二经验贴
  15. 【一篇文章告诉你网格策略从理论到实盘的所有内容(python实现)】
  16. dataV 自定义组件开发(个人信息展示 自动轮播)
  17. 晨枫U盘维护工具V2.0版(转)
  18. 台湾学计算机软件方面报什么专业,想请问各位,台湾哪间大学读软件工程比较好??...
  19. 从MIT的新式无人船,聊聊机器人的“组队打野”模式
  20. 权威解读GitHub、Apache疑云:主流开源软件究竟是否会被闭源?| 独家

热门文章

  1. 支教日记软件测试,教学实习每周工作计划精选 .doc
  2. 联想微型计算机设置从u盘启动,联想台式机设置从u盘启动图文教程
  3. 计算机与代数---如何计算log---方法推理[1]
  4. Excel for mac 快速选中大量的数据
  5. SQL查询条件传入参数为空查询全部数据,不为空指定查询,可选参数查询
  6. 快速上手Onvif(Python)心得
  7. C语言——计算标准差公式
  8. 抖音电商广告后台分为哪些?它们其中又有什么区别?
  9. Android 中 RecyclerView 的基本使用
  10. 普通java类注入dao失败_spring mvc 整合mybatis dao接口注入失败