Страница 1 из 1

ATmega32+TDA1543

Добавлено: Вт окт 28, 2014 14:06:37
Andrey27
Доброго времени суток! Решил воспроизвети звук wav с microsd, сначала с помощю шим на ATmega, (моно, стерео, разные битрейты) все отлично играет. Потом подсоединил TDA1543 для проверки воспроизведения через ее. Дописал процедуру передачи двух байт по I2S как в даташите описано. В итоге играет но звук какбудто очень сильно тянет пленку) Подумал не хватает скорости для передачи програмно I2S, поменял кварц с 14Мгц на 16Мгц не помогло. Передавал 1 байт моно на 1 канал не помогло. Может кто работал с TDA1543, посмотрите код передачи где я ошибся:

Код: Выделить всё

#include <mega32.h>
#include <delay.h>
#include "diskio.h"
#include "pff.h"

#define _XTAL_FREQ 16000000

#define WS    1
#define BCK   0
#define DATA  2

#define Set_0_DATA   PORTA &= ~(1 << DATA) 
#define Set_1_DATA   PORTA |= (1 << DATA)
#define BeginTakt    PORTA &= ~(1 << BCK)   
#define EndTakt      PORTA |= (1 << BCK)
#define Select_A     PORTA &= ~(1 << WS)
#define Select_B     PORTA |= (1 << WS)
#define Init_I2S     DDRA |= (1 << WS)|(1 << BCK)|(1 << DATA); PORTA |= (1 << WS)|(1 << BCK)|(1 << DATA); Select_A

unsigned char buff[512];     
volatile unsigned int n = 0;

void Write_Data(unsigned char DataR, unsigned char DataL)
{
 unsigned char i;
   Select_A;           
   for (i = 0; i < 8; i++)       
   {                           
    if ((DataR & 0x80) == 0x00)   
     Set_0_DATA;                 
    else                   
     Set_1_DATA;               
    DataR = DataR << 1;           
    BeginTakt;                     
    #asm("nop");           
    EndTakt;               
   }
   Set_0_DATA;
   for (i = 0; i < 8; i++)
   {
    BeginTakt;
    #asm("nop");
    EndTakt;
   }
   Select_B; 
   for (i = 0; i < 8; i++)       
   {                           
    if ((DataL & 0x80) == 0x00)   
     Set_0_DATA;                 
    else                   
     Set_1_DATA;               
    DataL = DataL << 1;           
    BeginTakt;                     
    #asm("nop");           
    EndTakt;               
   }
   Set_0_DATA;
   for (i = 0; i < 8; i++)
   {
    BeginTakt;
    #asm("nop");
    EndTakt;
   }   
}
                         
interrupt[TIM0_OVF] void Timer0_OVF_8bit (void)
 {
   Write_Data(buff[n], buff[n]);  // Предпологаю что ошибка тут.
   n++;                 
   if (n > 511) n = 0;
   TCNT0 = 0xC5;           //~32000 Гц
 }

void Play(char *Name)
 {
  WORD bt;
  unsigned char p = 0;   
  pf_open(Name);
  TIMSK = 0x01;   
  while(1)
   {
    if (!p && n > 255)
     {                     
      pf_read(&buff[0], 256, &bt); 
      p = 1;
      if (bt < 256) break;   
     } 
     if (p && n < 256)
     {
      pf_read(&buff[256], 256, &bt);
      p = 0;
      if (bt < 256) break;         
     }
   }
  TIMSK = 0x00;         
 }

void main(void)
{
 FATFS FS;
 DDRA = 0xff;
 PORTA = 0x00;
 TCCR0 = 0b00000010; 
 Init_I2S;
 delay_ms(100); 
 if (disk_initialize() == 0) if (pf_mount(&FS) == 0) if (pf_open("1.wav") == 0) PORTA.3 = 1;
 delay_ms(1000);
 #asm("sei")
 Play("1.wav");
 while(1)
 {
  PORTA.3 = !PORTA.3;
  delay_ms(1000);
 }
}

Re: ATmega32+TDA1543

Добавлено: Ср окт 29, 2014 13:31:22
mail_robot
судя по коду протормаживает проц. Нужен аппаратный I2C или переписывать WriteData на асме. Очень она тугая.
чисто для эксперимента позаменять все циклы for на while
в условниках поубирать условие ==0x00, заменить на if(!(var&const)), а лучше вообще обойтись без условного оператора отправляя бит по маске. И тому подобный тормозной мусор почистить.
если функция вызывается только в прерывании, нафига ее было было определять как функцию? Переставить ее текст целиком в прерывание. Уберется куча лишнего кода. Ну или определить ее как inline чтоли....
После этого померить как поменяется частотка на интерфейсе

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 07:53:46
mail_robot
чето думал думал... и все таки лучше использовать аппаратный I2C (2 wire serial interface)
тем более что у 32-й меги он штатно есть

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 08:07:38
Engineer_Keen
Там не I2C, там I2S, некое подобие SPI, но вместо линии CS, некий сигнал WS, который определяет для какого канала данные. В принципе, идею перехода на аппаратный интерфейс (SPI в меге тоже есть) это не отменяет.

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 08:11:17
mail_robot
согласен

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 12:28:59
Andrey27
Спасибо за советы. На аппаратном SPI microsd висит. Проблему со скоростью решил но качество воспроизведения ужас) Есть не понятные моменты с передачей I2S (по даташиту не работает а по моему работает но со щелчками и пиками). Я думаю сначала нужно написать программу для вывода синуса на 2 канала через TDA1543 и посмотреть на осцилографе, потестить до какой частоты передачи на выходе норм синус.

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 12:46:21
Engineer_Keen
Andrey27 писал(а):работает но со щелчками и пиками

Возможно периодически возникает какой-то тупняк при работе в SD картой. Идея с синусом действительно может помочь в проверке, раз в наличии есть осциллограф.

Re: ATmega32+TDA1543

Добавлено: Чт окт 30, 2014 15:40:12
mail_robot
что решит наличие синуса? Тут надо смотреть где тупняки возникают. SPI вообщет не только на карточку можно зацепить, есть же еще вариант с выбором адресата, не? Тут дело то в другом. Когда мы читаем в буфер, то слать непрерывно на ЦАП возможности уже нет. Следовательно - возникают паузы на потоке передачи. Вот и слышны щелчки. Посему выходом может быть только одно: читаем с карты в буфер пока можем до прерывания таймера дискретизации звука, а потом сразу на ЦАП надо передавать строго по жесткому таймингу. Потом переключаемся сразу на чтение. если в буфере есть место. По заполнению останавливаемся и ждем прерывания таймера. Шлем в ЦАП. И так далее. Тогда звук станет гладким.
Можно перед каждым циклом чтения с карты проверять сколько таймеру осталось до прерывания. Если ему меньше чем нам нужно на кадр чтения, то не читаем, а ждем прерывание на отправку. Потом запускаем чтение сколько можем. Приоритизируем посылку короче говоря с гарантией
Быстродействия карточки должно хватать на заполнение буфера со скоростью раза в 2-3 выше, чем мы отправляем дискрету на ЦАП.
Арбитраж короч грамотный нужен в системе. Сейчас происходят банальные коллизии от прерываний аппаратного SPI, но с неправильной стороны.

Re: ATmega32+TDA1543

Добавлено: Пт окт 31, 2014 08:13:48
Engineer_Keen
mail_robot писал(а):что решит наличие синуса?

Так можно оставить вывод звука исключив из системы работу с картой. Если никаких щелчков не будет, значит дело действительно в работе с картой и не совсем верным распределением времени между задачами.

Re: ATmega32+TDA1543

Добавлено: Пт окт 31, 2014 09:12:34
mail_robot
ну в принципе, если есть желание помучиться, то можно успокоить душу. Но я лично начал бы с осциллографа. Взял бы свободную ногу контроллера и каждую посылку байта на ЦАП отмечал бы импульсом (пара строчек кода). Потом на эту ногу зацепил осцил и посмотрел бы равномерность следования этих импульсов. Это даст куда больше инфы, чем синус. По частоте следования можно сразу оценить максимально возможную дискретизацию при таком коде и посмотреть насколько код выполняется равномерно. Нет ли больших пауз. Ну и дальше уже развить

Re: ATmega32+TDA1543

Добавлено: Пн ноя 03, 2014 20:33:52
Andrey27
Переписал код без всяких буферов, таймеров и файловой системы, 1 байт считал с карточки и тут же его послал на цап и т.д. Результат: скорости хватает спокойно проигрывать
44100, 8bit, stereo 705kbit/sec. Но странный завал на низких частотах. Подключил выход цапа к компу и записал: http://dropmefiles.com/Uh5dZ
Проверил генерацию меандра и пилы, вроде все нормально:

Код: Выделить всё

#include <mega32.h>
#include <delay.h>

#define _XTAL_FREQ 16000000
                         
#define DDRX  DDRA
#define PORTX PORTA  // Настройка порта (порт A)
#define WS    1      // Выход переключения канала (1 пин)
#define BCK   0      // Выход тактового сигнала (0 пин)
#define DATA  2      // Выход данных (2 пин)

#define Set_0_DATA   PORTX &= ~(1 << DATA)  // Установка "0" на линии данных
#define Set_1_DATA   PORTX |= (1 << DATA)   // Установка "1" на линии данных
#define BeginTakt    PORTX &= ~(1 << BCK)   // Установка "0" на линии тактирования
#define EndTakt      PORTX |= (1 << BCK)    // Установка "1" на линии тактирования
#define Select_A     PORTX &= ~(1 << WS)    // Установка "0" на линии выбора канала
#define Select_B     PORTX |= (1 << WS)     // Установка "1" на линии выбора канала
#define Init_I2S     DDRX |= (1 << WS)|(1 << BCK)|(1 << DATA); PORTX &= ~((1 << WS)|(1 << BCK)|(1 << DATA));  // Настройка порта   

void Write_Data(unsigned char DataR, unsigned char DataL)
{
  unsigned char i;
   Select_A; 
   for (i = 0; i < 8; i++)       
   {
    BeginTakt;                         
    if ((DataR & 0x80) == 0x00)   
     Set_0_DATA;                 
    else                   
     Set_1_DATA;               
    DataR = DataR << 1;             
    EndTakt;   
   }
   Select_B;
   for (i = 0; i < 8; i++)       
   {
    BeginTakt;                           
    if ((DataL & 0x80) == 0x00)   
     Set_0_DATA;                 
    else                   
     Set_1_DATA;               
    DataL = DataL << 1;
    EndTakt;
   }             

       
void main(void)                 
{
  unsigned char j = 0;
   Init_I2S;
   while(1)
   {
    j++;                      // Пила
    Write_Data(j, j);
    delay_us(10);
  /*Write_Data(0xff, 0xff);   // Меандр
    delay_us(500);
    Write_Data(0x00, 0x00);
    delay_us(500);   */
   }
}

Изображение
Изображение
Подбирал резисторы на выходах цап и Vref не помогло, схема подключения цап:
Изображение
Даже не знаю что еще можно сделать.

Re: ATmega32+TDA1543

Добавлено: Вт ноя 04, 2014 08:02:57
mail_robot
ну собственно что и требовалось доказать
глюк в арбитраже (ну то есть в алгоритме по сути)
поздравляю с успешным пуском

думаю что не нужно ждать от дешовенького цапа космического качества на выходе и суперскоростей установки напряжения