Генератор сигналов может генерировать различные сигналы, такие как синусоидальные, прямоугольные и пилообразные. У некоторых из них имеется функция развертки и организации произвольной формы сигнала. Это полезные инструменты в мастерской. Их можно использовать для проверки аудиосхем, схем на основе операционных усилителей и проверки отклика схемы. Большинство современных функциональных генераторов могут легко выдавать частоты до 1 МГц.

Если вам нужен простой генератор синусоидального сигнала, но неохота тратить денег на полноценный генератор сигналов, то можно сделать такой своими руками на основе Arduino, и в данном материале будет рассказано как.
Наш простой генератор синусоидального сигнала основан на Я решил, справочной таблицы значений, которые определяют синусоидальном выходе. Чем больше значений вы укажете в таблице, тем лучше выходной сигнал будет приближаться к истинной синусоиде. Также очень удобно использовать степень двойки при создании такой таблицы. Важно также выбрать количество значений. Так, 8 значений не дадут требуемого результата, а 128, вероятно, будут нагружать ядро микроконтроллера, поэтому мы выбрали 32 значения для начала в качестве оптимального варианта.
Для формирования выходного сигнала мы взяли ЦАП MCP4725, который представляет собой 12-битное устройство. На следующем изображении представлена схема подключения Arduino к ЦАП по интерфейсу I2C.


Для формирования таблицы был открыт Excel, куда были записаны числа от 0 до 31. Следующим столбцом, который нам нужен, был угол в радианах. Это просто индекс, умноженный на 2, умноженный на Pi, деленный на 32. Следующий столбец – это нормализованная амплитуда сигнала. Мы просто взяли синус угла из предыдущего столбца. Это создало сигнал, который составлял 2 единицы от пика до пика и был сосредоточен на нуле. Его максимум был +1, а минимум -1.
Имея выходной диапазон от 0 до 5 В, нам понадобился сигнал с центром на 2,5 В и амплитудой ±2,5 В. Следующий столбец просто умножил сигнал на 2,5. Затем компенсируем его на 2,5 В. Сигнал показал максимум 5В и минимум 0В. Теперь нам просто нужно было преобразовать это в 12-битное число, чтобы поместить в нашу таблицу. Поскольку мы хотели, чтобы значение было 4095 при напряжении 5 В, мы умножаем на 4095 и делим на 5. Чтобы проверить, посмотрите на строку 8. Выходное напряжение составляет 5 В, а количество битов – 4095.
32 2-байтовых числа – это не так много данных, и их можно легко сохранить в ОЗУ, но мы будем сохранять таблицу во FLASH и читать ее по мере необходимости. Преимущество в том, что если мы решим повысить точность сигнала и добавить больше данных, нам может не хватить ОЗУ и нужно будет использовать флэш-память. В итоге воспользуемся командой PROGMEM. Код программы нашего генератора синусоидального сигнала на основе Arduino следующий:
#include<Wire.h>
#include<avr/pgmspace.h>
#define addr 0b1100011
#define Conver_reg 0X00
#define Config_reg 0x01
#define Low_lim_reg 0X02
#define High_lim_reg 0X03
#define FM 0x00
#define LD 0x02
#define DaEP 0x03
unsigned char incomingByte = 0;
unsigned char DataH = 0;
unsigned char DataL = 0;
unsigned char Cmd = 0x00;
unsigned char PDM = 0;
unsigned char Array[6] = {0, 0, 0, 0, 0};
unsigned char i = 0;
unsigned char val = 0;
unsigned int temp = 0;
unsigned char wait = 0;
unsigned char x = 0;
const PROGMEM unsigned int mysine[] = {
2048,
2447,
2831,
3185,
3495,
3750,
3939,
4056,
4095,
4056,
3939,
3750,
3495,
3185,
2831,
2447,
2048,
1648,
1264,
910,
600,
345,
156,
39,
0,
39,
156,
345,
600,
910,
1264,
1648
};
void setup() {
Wire.begin();
Serial.begin(9600);
banner();
}
void loop() {
if (Serial.available() > 0)
{
incomingByte = Serial.read();
//====================
if (incomingByte == '1')
{
incomingByte = 0;
Wire.requestFrom(addr, 5, 1);
i = 0;
while (Wire.available())
{
i++;
Array[i] = Wire.read();
}
Serial.print("\n This is the Command Byte: "); Serial.print(Array[1], HEX);
Serial.print("\n DAC Reg MSB Byte: "); Serial.print(Array[2], HEX);
Serial.print("\n DAC REG LSB Byte: "); Serial.print(Array[3], HEX);
Serial.print("\n EEPROM Power down + MSB Byte: "); Serial.print(Array[4], HEX);
Serial.print("\n EEPROM LSB Byte: "); Serial.println(Array[5], HEX);
banner();
}
//====================
if (incomingByte == '2')
{
incomingByte = 0;
Serial.print("\nSelect the power down mode\n");
Serial.print("1 - Normal Mode (0)\n");
Serial.print("2 - 1K Resistor to GND (1)\n");
Serial.print("3 - 100K Resistor to GND (2)\n");
Serial.print("4 - 500K Resistor to GND (3)\n");
wait = 1;
PDM = 0;
while (wait) {
if (Serial.available() > 0)
{
incomingByte = Serial.read();
if ((incomingByte > 47) && (incomingByte < 53)) {
wait = 0;
}
}
}
switch (incomingByte) {
case 49:
PDM = 0x00;
break;
case 50:
PDM = 0x01;
break;
case 51:
PDM = 0x02;
break;
case 52:
PDM = 0x03;
break;
}
incomingByte = 0;
Serial.print("\nPDM mode: ");
Serial.print(PDM);
Serial.print("\n");
banner();
}
//====================
if (incomingByte == '3')
{
incomingByte = 0;
Serial.print("\nSelect the Command Mode\n");
Serial.print("1 - Fast (0)\n");
Serial.print("2 - Write to DAC (2)\n");
Serial.print("3 - Write to DAC and EEPROM (3)\n");
wait = 1;
Cmd = 0;
while (wait) {
if (Serial.available() > 0)
{
incomingByte = Serial.read();
if ((incomingByte > 48) && (incomingByte < 52)) {
wait = 0;
}
}
}
switch (incomingByte) {
case 49:
Cmd = 0x00;
break;
case 50:
Cmd = 0x02;
break;
case 51:
Cmd = 0x03;
break;
}
Serial.print("\nCMD mode: ");
Serial.print(Cmd);
Serial.print("\n");
banner();
}
//====================
if (incomingByte == '4')
{
incomingByte = 0;
Serial.print("\nEnter a value between 0 and 4095\n");
Serial.print("You must use leading zeros\n");
Serial.print("Value: ");
wait = 1;
i = 0;
while (wait) {
if (Serial.available() > 0)
{
incomingByte = Serial.read();
if ((incomingByte) != 10) {
i++;
Array[i] = (incomingByte - 48);
if (i >= 4) {
wait = 0;
}
}
}
}
temp = 0;
temp = 1000 * Array[1] + 100 * Array[2] + 10 * Array[3] + Array[4];
if (temp > 4095) {
Serial.print("\nError: Value too large -> ");
Serial.print(temp);
}
else {
DataL = ((temp << 4) & 0x00F0);
DataH = ((temp >> 4) & 0x00FF);
}
Serial.print("\n");
Serial.print(temp);
Serial.print("\n");
banner();
}
//====================
if (incomingByte == '5')
{
incomingByte = 0;
if (Cmd & 0x06)
{
val = (((Cmd << 5) & 0xE0) | ((PDM < 1) & 0x06));
Wire.beginTransmission(addr);
Wire.write(val);
Wire.write(DataH);
Wire.write(DataL);
Wire.endTransmission();
Serial.print("\n MSB: "); Serial.print(DataH, HEX);
Serial.print("\n LSB: "); Serial.println(DataL, HEX);
}
else
{
val = (((PDM << 4) & 0x30) | (( DataH >> 4) & 0x0F));
Wire.beginTransmission(addr);
Wire.write(val);
val = (((DataH << 4) & 0xF0) | ((DataL >> 4) & 0x0F));
Wire.write(val);
Wire.endTransmission();
Serial.print("\nFast MSB: "); Serial.print(DataH, HEX);
Serial.print("\n LSB: "); Serial.println(DataL, HEX);
}
banner();
}
//====================
if (incomingByte == '6')
{
incomingByte = 0;
Serial.print("\nOutputs a sine wave\n");
Serial.print("Enter the 'e' key to exit\n");
Serial.print("+ to increase period and - to decrease period\n");
Serial.print("Delay time will be printed to Serial Monitor \n");
Serial.print("Outputing wave now\n");
wait = 1;
i = 0;
while (wait)
{
if (Serial.available() > 0)
{
incomingByte = Serial.read();
if ((incomingByte == 'e') | (incomingByte == 'E')) {
wait = 0;
}
if (incomingByte == 43) {
x++;
Serial.println(x);
}
else if (incomingByte == 45) {
x--;
Serial.println(x);
}
incomingByte = 0;
}
delay(x);
temp = pgm_read_word_near(mysine + i);
//temp = (mysine[i]);
DataL = ((temp << 4) & 0x00F0);
DataH = ((temp >> 4) & 0x00FF);
val = (((PDM << 4) & 0x30) | (( DataH >> 4) & 0x0F));
Wire.beginTransmission(addr);
Wire.write(val);
val = (((DataH << 4) & 0xF0) | ((DataL >> 4) & 0x0F));
Wire.write(val);
Wire.endTransmission();
i++;
i = i & 0x1F;
}
banner();
}
//====================
if ((incomingByte == 'H') | (incomingByte == 'h'))
{
incomingByte = 0;
Serial.print("\n");
banner();
}
}
}
void banner() {
Serial.print ("\nDigital to Analog Converter SF-5: Main Menu\n\n");
Serial.print ("1. Read DAC Registers\n");
Serial.print ("2. Power Down Select\n");
Serial.print ("3. Command Mode Select\n");
Serial.print ("4. Get value to write\n");
Serial.print ("5. Execute a write\n");
Serial.print ("6. Sine wave output\n");
Serial.print ("H. Help Menu\n");
}
Максимальная частота, которую вы получите, составляет 92 Гц. Для настройки частоты используются delay() после каждой записи в таблице. Это будет 32 мс (примерно) за цикл. Итак, следующие частоты - 23 Гц, 13 Гц, 9 Гц. Вы можете увеличить задержку до 256. Это даст период в 8 секунд.

Если вам не нравится качество выходного сигнала, вы можете либо увеличить количество значений в таблице, либо сгладить этот сигнал, используя фильтр. То есть мы можем отфильтровать высокие частоты, которые создают острые углы сигнала при изменении уровней напряжения. Например, мы использовали простой RC-фильтр низких частот со значениями R = 10K, C = 0,1 мкФ для организации частоты среза около 160 Гц. На следующем изображении показан результат фильтрации, где мы видим, что сигнал выглядит намного лучше и является лучшим приближением к истинной синусоиде.

© digitrode.ru