シリアルポートとしてのサウンドカード

最近のPCには、使いやすいインターフェイスがないという問題があります。 USBを使用するには大量の複雑なコードが必要ですが、UARTにはUSB-COMアダプターが必要です。 外部デバイスが単純な場合、インターフェースの開発はデバイス自体の開発よりも時間がかかる場合があります。 同時に、多くのデバイスには、オーディオデバイス用のアナログインターフェイスがあり、変更なしでデータを入力または出力できます。 STM32VLDISCOVERYボードからWindows XPを搭載したPCへのマイク入力によるデータ入力の例を次に示します。 インターフェイスは純粋にデジタルではなく、デジタルアナログです。 ボードからのデータは、コントローラーのDACを介して、異なる振幅の4つの矩形パルスのバッチで送信されます。 パルス繰り返し率は、ほとんどのサウンドカードの入力アンプの上限周波数(20 kHz)に対応しています。 パックの始まりは、幅が2倍のパルスでマークされます。 次の3つのパルスには、パルスの振幅に埋め込まれた情報が含まれています。 4ビット振幅コーディングのデータ転送速度は約45 kbit / sです。



ファームウェアSTM32VLDISCOVERYのコード:



#include "stm32f10x.h" #define DAC_DHR12RD_Address 0x40007420 #define BUF_SIZE 640 /* Init Structure definition */ DAC_InitTypeDef DAC_InitStructure; DMA_InitTypeDef DMA_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; /* Private variables ---------------------------------------------------------*/ uint32_t DualSine12bit[BUF_SIZE], Idx = 0, Idx2 = 0, Idx3 = 0, a1,a2,a3,a4, cc; int RR; double R; /* Private function prototypes -----------------------------------------------*/ void RCC_Configuration(void) { /* DMA1 clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); /* GPIOA Periph clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* DAC Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); /* TIM2 Periph clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); } void Timebase_Configuration(void) { TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 0x120; //0x04; 0x150; TIM_TimeBaseStructure.TIM_Prescaler = 0x01; TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* TIM2 TRGO selection */ TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); /* TIM2 enable counter */ TIM_Cmd(TIM2, ENABLE); } void DAC_Configuration() { /* DAC channel1 Configuration */ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //Enable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); /* DAC channel2 Configuration */ DAC_Init(DAC_Channel_2, &DAC_InitStructure); /* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is automatically connected to the DAC converter. */ DAC_Cmd(DAC_Channel_1, ENABLE); /* Enable DAC Channel2: Once the DAC channel2 is enabled, PA.05 is automatically connected to the DAC converter. */ DAC_Cmd(DAC_Channel_2, ENABLE); /* Enable DMA for DAC Channel2 */ DAC_DMACmd(DAC_Channel_2, ENABLE); } void DMA_Configuration() { /* DMA1 channel4 configuration */ DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&DualSine12bit; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUF_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); /* Enable DMA1 Channel4 */ DMA_Cmd(DMA1_Channel4, ENABLE); } void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } void Point(uint32_t kx, uint32_t ky, uint32_t ki) { for (Idx2 = 0; Idx2 < BUF_SIZE-10; Idx2++) { DualSine12bit[Idx2+10] = DualSine12bit[Idx2]; } DualSine12bit[0] = 4095; DualSine12bit[1] = 4095; DualSine12bit[2] = 0; DualSine12bit[3] = 0; DualSine12bit[4] = 2096 + kx; DualSine12bit[5] = 2000 - kx; DualSine12bit[6] = 2096 + ky; DualSine12bit[7] = 2000 - ky; DualSine12bit[8] = 2096 + ki; DualSine12bit[9] = 2000 - ki; } /* Private functions ---------------------------------------------------------*/ int main(void) { /* System Clocks Configuration */ RCC_Configuration(); /* Once the DAC channel is enabled, the corresponding GPIO pin is automatically connected to the DAC converter. In order to avoid parasitic consumption, the GPIO pin should be configured in analog */ GPIO_Configuration(); /* TIM2 Configuration */ /* Time base configuration */ Timebase_Configuration(); /* DAC channel1 Configuration */ DAC_Configuration(); /* DMA1 channel4 configuration */ DMA_Configuration(); R = 1; RR=1; a1 = 2023; a2 = 1000; a3 = 100; a4 = 900; while (1) { for (Idx = 0; Idx < 32*32; Idx++) { Idx3 = Idx/32; Point((Idx-Idx3*32)*50,Idx3*50,Idx/32*50); } } }
      
      







PCアプリケーションコード:



 #include <stdlib.h> #include <stdio.h> #include <conio.h> #include <string.h> #include <fstream.h> #include <iomanip.h> #include <windows.h> #include <math.h> #include <vcl.h> #include <mmsystem.h> #include "mainF_dbl.h" #pragma hdrstop #pragma package(smart_init) #pragma resource "*.dfm" #define INP_BUFFER_SIZE 16384 #define SAMPLE_RATE 192000 TForm1 *Form1; static HWAVEIN hWaveIn = NULL; static WAVEHDR WaveHdr1, WaveHdr2; static WAVEFORMATEX waveformat ; static unsigned short Buffer1[INP_BUFFER_SIZE], Buffer2[INP_BUFFER_SIZE], saveBuffer[INP_BUFFER_SIZE]; static signed int RR, saveBuffer2[INP_BUFFER_SIZE]; static BOOL bEnding, bGraph, flag; BOOL bShutOff; long int RR_max, RR_min, LLL; int ix, iy, iz, k, kx, ky, m, kp ; void CALLBACK waveInProc1(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { switch(uMsg) { case WIM_OPEN: break; case WIM_DATA: CopyMemory (saveBuffer, ((PWAVEHDR) dwParam1)->lpData, ((PWAVEHDR) dwParam1)->dwBytesRecorded) ; if (bEnding){ waveInReset (hWaveIn); waveInClose (hWaveIn); return; } waveInAddBuffer (hwi, (PWAVEHDR) dwParam1, sizeof (WAVEHDR)) ; // Send out a new buffer break; case WIM_CLOSE: waveInUnprepareHeader (hWaveIn, &WaveHdr1, sizeof (WAVEHDR)) ; waveInUnprepareHeader (hWaveIn, &WaveHdr2, sizeof (WAVEHDR)) ; } } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx void __fastcall TForm1::startButtonClick(TObject *Sender) { bGraph=false; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx void __fastcall TForm1::formDestroy(TObject *Sender) { bEnding=false; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx void __fastcall TForm1::Button1Click(TObject *Sender) { bGraph=true; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { waveformat.wFormatTag = WAVE_FORMAT_PCM ; waveformat.nChannels = 1; //2 ; waveformat.wBitsPerSample = 16 ; waveformat.nSamplesPerSec = SAMPLE_RATE ; waveformat.nBlockAlign = waveformat.nChannels * (waveformat.wBitsPerSample / 8); waveformat.nAvgBytesPerSec = waveformat.nBlockAlign * waveformat.nSamplesPerSec; waveformat.cbSize = 0 ; if (waveInOpen (&hWaveIn, WAVE_MAPPER, &waveformat, (DWORD)waveInProc1, 0, CALLBACK_FUNCTION)){ Application->MessageBox( "000000000","Error",MB_OK ); return; } bShutOff=false; // Set up headers and prepare them WaveHdr1.lpData = (BYTE *)Buffer1 ; WaveHdr1.dwBufferLength = INP_BUFFER_SIZE*2 ; // WaveHdr1.dwBytesRecorded = 0 ; WaveHdr1.dwUser = 0 ; WaveHdr1.dwFlags = 0 ; WaveHdr1.dwLoops = 1 ; WaveHdr1.lpNext = NULL ; WaveHdr1.reserved = 0 ; waveInPrepareHeader (hWaveIn, &WaveHdr1, sizeof (WAVEHDR)) ; WaveHdr2.lpData = (BYTE *)Buffer2 ; WaveHdr2.dwBufferLength = INP_BUFFER_SIZE*2 ; // WaveHdr2.dwBytesRecorded = 0 ; WaveHdr2.dwUser = 0 ; WaveHdr2.dwFlags = 0 ; WaveHdr2.dwLoops = 1 ; WaveHdr2.lpNext = NULL ; WaveHdr2.reserved = 0 ; waveInPrepareHeader (hWaveIn, &WaveHdr2, sizeof (WAVEHDR)) ; waveInAddBuffer (hWaveIn, &WaveHdr1, sizeof (WAVEHDR)) ; waveInAddBuffer (hWaveIn, &WaveHdr2, sizeof (WAVEHDR)) ; waveInStart (hWaveIn) ; bGraph=true; bEnding = FALSE; } void __fastcall TForm1::Timer1Timer(TObject *Sender) { if (bGraph){ kp++; if (kp>20){kp=0; Canvas->Brush->Color = Color; Canvas->FillRect(Rect(0,0,512,512));} k=0; m=0; RR_min=0; RR_max=0; kx=0; ky=0; for(int LLL=0; LLL<INP_BUFFER_SIZE; LLL++) { short)(saveBuffer[LL*2]); RR = (signed short)(saveBuffer[LLL]); if (RR > 0) { if(RR_max < RR) RR_max = RR; if((kx>6)&&(RR_min<30000)) { //&&(k==0)){ m=0; } if(RR_min < 0) { if (m==1) ix = -RR_min*16/1024; if (m==2) iy = -RR_min*16/1024; if (m==3) iz = -RR_min*4/512; } flag=false; kx=0; RR_min = 0; ky++; } if (RR < 0) { if(RR_min > RR) RR_min = RR; if (ky>6){ if (m==3) {Canvas->Brush->Color = TColor(RGB(iz, iz, iz)); Canvas->FillRect(Rect(ix,iy,ix+16,iy+16));} } if(!flag) m++; RR_max = 0; flag=true; ky=0; kx++; } } } }
      
      







フォームには、「停止」と「実行」の2つのボタンと正方形のフィールドがあり、座標xとyの位置は最初の2つのパルスの振幅によって決まり、輝度は3番目の振幅によって決まります。 ボードは電話線で接続され、PC側にはモノ用の標準ジャックがあり、STM32VLDISCOVERY側にはPA.04ピンがエミッターフォロア(STM32VLDISCOVERYではDAC出力が高抵抗)とキャリブレーション用のディバイダー(可変抵抗器)を介して接続されます。







STM32VLDISCOVERYからのマイク入力を介してPCに送信されたテスト画像(上から下に正方形の輝度の勾配がある32x32正方形フィールド):






All Articles