который генерируется аппаратно, а не програмно. Начало передачи по аппаратному SPI инициирует программа, это мне не подходило из-за interrupt latency. Поэтому я использую внешнюю логику, которая "врезает" дополнительный сигнал CLK после переключения WS. Этот сигнал генерируется со строгой периодичностью с помощью OC1A, это как раз и есть период обновления ЦАП. В промежутках между этими сигналами с помощью аппаратного SPI загружаю 16 бит для одного канала (второй канал не используется).
;Прерывание по Compare таймера 1:
DDS: ldi XL,(1<<COM1A0)
out TCCR1A,XL ;включили режим переключения
;выхода OC1A
ldi XL,(1<<COM1A0) | (1<<FOC1A)
out TCCR1A,XL ;делаем force toggle, в результате
;BCK сбросится в 0 (с учетом инверсии логики).
Port_I2SWS_0 ;переключаем WS обратно в 1
;(с учетом инверсии логики).
out TCCR1A,XL ;force toggle, возвращаем BCK в 1
out SPDR,SinH ;---> load DAC high byte
in tsreg,SREG ;save status register
;здесь делаю вычисления, пока байт передается
;(готовлю новое значение SinH для следующего цикла)
out SPDR,SinL ;---> load DAC low byte
;здесь делаю вычисления, пока байт передается
;(готовлю новое значение SinL для следующего цикла)
ph_all: ldi XL,(1<<COM1A0) | (1<<FOC1A)
out TCCR1A,XL ;force toggle, устанавливаем BCK в 0
out SPDR,r0 ;пустая передача просто
;для того, чтобы обеспечить на линии данных какой-то
;неизменный уровень на момент поступления дополнительного
;клока. Иначе по дополнительному клоку в старший
;бит второго канала загружается то, что в это время
;висит на данных (а там младший байт предыдущего канала).
;Как оказалось, каналы довольно сильно влияют друг на друга,
;дергающийся старший бит второго канала виден на первом.
;Вместо передачи по SPI можно просто установить
;нулевой уровень на линии данных. Но мне время позволяло
;сделать фиктивную передачу по SPI, а это минимальное
;количество кода в прерывании.
Port_I2SWS_1 ;устанавливаем WS в ноль
;(с учетом инверсии логики)
ldi XL,(1<<COM1A1)
out TCCR1A,XL ;настраиваем OC1A на clear on compare,
;в результате в момент совпадения на BCK будет сгенерирован
;переход 0->1 (с учетом инверсии логики), который обновит ЦАП.
out SREG,tsreg ;restore status register
reti
;Настройка таймера и SPI:
ldi temp,(1<<WGM12) | (1<<CS10)
out TCCR1B,temp ;clear on compare match, CK/1
ldi temp,(1<<COM1A1)
out TCCR1A,temp ;OC1A clear on compare
ldi temp,high(FCLK/FUPD-1)
out OCR1AH,temp
ldi temp, low(FCLK/FUPD-1)
out OCR1AL,temp
ldi temp,(1<<OCF1A)
out TIFR,temp ;clear pending timer interrupt
out TIMSK,temp ;enable output compare interrupt
ldi temp,(1<<SPE) | (1<<MSTR) | (1<<CPHA)
out SPCR,temp ;SPI enable, MSB first, master
ldi temp,(1<<SPI2X)
out SPSR,temp ;double SPI speed