声卡
声卡设计¶
现在我们要设计一个声卡,必要的程序是stm32端和上位机程序。我推荐使用MATLAB,因为很多东西已经被封装好了,很方便,这里我选用C语言来实现。
第一步¶
弄明白如何使用串口通讯,能做到利用串口收发信息(计算机到stm32)。这里推荐 Windows 应用商店里的串口助手,可以很方便的实现串口通讯。不过某些时候这个串口助手没办法识别虚拟串口,因此需要其他的串口助手。
第二步¶
弄明白什么是 ADC 和 DAC, 并初步实现 ADC 和 DAC 的采样和输出。
做完这一步你就应该能明白这整个项目是在干什么了,
- 用 DAC 采样,实时发送数据到计算机
- 计算机接收数据后存储成 wav 格式
- 计算机发送数据到 stm32,stm32 通过 ADC 采样
- stm32 通过 DAC 输出
思路如上,单片机侧的程序比较简单,照着例程改改就可以用,但需要注意波特率设置,尽量高,比如2000000,这样可以保证数据的实时性。采样率设置10k就够了,更高当然也可以。
第三步¶
接下来就是计算机侧的程序
这里用C实现串口读写和wav格式存储。这里需要注意的是,wav格式的头文件需要自己写,不过网上有很多现成的,可以直接拿来用。
首先是串口读写
声明变量
HANDLE hSerial;
 DCB dcbSerialParams = { 0 };
 char port[] = "COM"; // specify the COM port you want to use
 DWORD bytes_written, bytes_read;
 uint16_t incoming_data[240000] = { 0 };   // buffer to store incoming data
打开串口
// open the serial port
 hSerial = CreateFileA(port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
 if (hSerial == INVALID_HANDLE_VALUE)
 {
  printf("Error opening serial port.\n");
  return 1;
 }
 // set the serial port parameters
 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
 if (!GetCommState(hSerial, &dcbSerialParams))
 {
  printf("Error getting serial port state.\n");
  CloseHandle(hSerial);
  return 1;
 }
 dcbSerialParams.BaudRate = 2000000;
 dcbSerialParams.ByteSize = 8;
 dcbSerialParams.StopBits = ONESTOPBIT;
 dcbSerialParams.Parity = NOPARITY;
 if (!SetCommState(hSerial, &dcbSerialParams))
 {
  printf("Error setting serial port state.\n");
  CloseHandle(hSerial);
  return 1;
 }
 int cnt = 0;
 if (!ReadFile(hSerial, incoming_data, sizeof(incoming_data), &bytes_read, NULL))
 {
  printf("Error reading from serial port.\n");
  CloseHandle(hSerial);
  return 1;
 }
这样就可以读取串口数据了。值得注意的是读取的时候记得使用CreatFileA,而不是CreatFile。
最后是wav文件写入
头文件
#define TEST_SAMPLE_RATE    10000
#define TEST_SAMPLE_LEN_SEC    2
#define TEST_SAMPLE_NUM 20000
#define CHAN_NO 2
#define SAMPLE_SIZE_IN_BYTE_CH_MONO    2
#define SAMPLE_SIZE_IN_BYTE_ALL_CH  4
#define BITS_PER_BYTE   8
#define SAMPLE_SIZE_IN_BITS_CH_MONO    (SAMPLE_SIZE_IN_BYTE_CH_MONO * BITS_PER_BYTE)
#define SAMPLE_SIZE_IN_BITS_ALL_CH (SAMPLE_SIZE_IN_BITS_CH_MONO * CHAN_NO)
SimpleWavHdr wavHdr;
FILE* fp = NULL;
 ZeroMemory(&wavHdr, sizeof(wavHdr));
 // Fill the header of the wave file
 wavHdr.RiffHdr = FOURCC_RIFF;
 wavHdr.ChunkSize = sizeof(wavHdr) - 8; //sizeof(wavHdr) - 8
 wavHdr.WavHdr = mmioFOURCC('W', 'A', 'V', 'E');
 wavHdr.FmtHdr = mmioFOURCC('f', 'm', 't', ' ');
 wavHdr.HdrLen = 16;
 wavHdr.DataType = WAVE_FORMAT_PCM;
 wavHdr.ChanNo = CHAN_NO;
 wavHdr.SamplePerSec = TEST_SAMPLE_RATE ;
 wavHdr.SamplePerSec = TEST_SAMPLE_NUM;
 wavHdr.BytePerSample = SAMPLE_SIZE_IN_BYTE_ALL_CH;
 wavHdr.BitsPerSample = SAMPLE_SIZE_IN_BITS_CH_MONO;
 wavHdr.dataHdr = mmioFOURCC('d', 'a', 't', 'a');
 wavHdr.RawSize = 960000;
 errno_t err = fopen_s(&fp, TEST_WAV_NAME, "wb");
 fwrite(&wavHdr, sizeof(wavHdr), 1, fp);
这里需要注意 wav 头格式的几个参数,比如采样率,采样位数等等,这些参数需要和单片机侧的参数一致。
记得关闭串口和文件
第四步¶
ADC 采样可以用 MATLAB ~(因为实在是太方便了)~别的语言搞通讯协议,比较复杂。所以 MATLAB 是最好的选择。
安装audio toolbox,剩下的留给读者。
第五步¶
变速不变调。
搜索 pitchshifter
单独改变wav文件的采样率同时改变速度和音调,大致的思路是在调整速度之前先加高或降低声音的音调,再调整速度就可以实现变速不变调。本次实验,可以采用 OLA 法 (Overlap-Add) 处理信号