Я надеялся, да что говорить был уверен, что уложусь в три части. Однако всё-таки будет четвертая. В прошлой части мы добились стабильно считывания TOC. И нестабильно, глючного, но всё-таки запуска игр. А также разобрались с тем, что же такое SENS, и как именно приставка выполняет позиционирование при помощи команд управление кареткой. Нам осталось реализовать модуль для эмуляции SENS. И решить маленькие, но важные мелочи. Если вам всё ещё интересно, чем всё это закончится, добро пожаловать под кат.
После того как я пришёл к выводу, что SENS это выход мультиплексора. Проблем с его реализацией не возникло. Хотя я очень долго пытался побороть желание всё это запихнуть в модуль CXD2545_CPU уж некоторые части слишком похожие были. Но потом решил, что лучше два маленьких хоть и похожих модуля, чем один огромный.
Ничего сложного тут нет, всё это я уже описывал и раньше. Только немного уточнений:
Модуль набирает данные по восемь бит, и в зависимости оттого, что получил, устанавливает на выход sens один из битов sens_data. Причем последнее действие делается каждый такт.
Чтобы управлять статусом этих пинов в систему был добавлен ещё один модуль PIO, на который и были подключены почти все пины SENS, кроме COUT:
На самом деле всё это, наверное, усложнено и есть куда более простой вариант, но меня пугали всякие слова типа метастабильность и синхронный дизайн.
Посмотрев снятые логи, я увидел, что приставка смотрит на COUT только когда использует «дальнее позиционирование» при помощи каретки. Поэтому было принято решение. Гнать туда меандр постоянно, но надо было угадать с частотой. Между двумя запросами COUT был промежуток примерно две миллисекунды, это где-то 250 герц максимум, что успеет воспринять приставка в таком темпе. Но это прям оочень максимум. Поэтому сразу сделал так чтобы можно было настраивать его, и по-хорошему наверно имело смысл повесить его на шину AvalonMM но было лень. В итоге родился такой модуль:
Как оно работает. На вход подается тактовая частота (у меня это 50 мегагерц), при помощи управляемого входа div мы получаем вторую частоту. Это как бы та частота, с которой мы пролетаем треки. Дальше при помощи toggle_cnt мы ещё раз делим полученную частоту, и получаем сигнал toggle_clk которые и идёт уже в модуль SENS. Но так как нам самим в коде надо будет знать, сколько треков мы насчитали, был добавлен сигнал trigger. И если был переход из низкого состояния в высокое, мы обнуляем счетчик треков, и начинаем их считать. А если был, наоборот, из высокого в низкий мы переносим подсчитанные треки в регистр track_count. Причем сам счетчик треков у нас размером в 31 бит, а 32ой бит используется как флаг того, что данные в регистре верные. Зачем так сделано? Мне было лень вешать этот модуль на шину AvalonMM и я его подключил к процессору через модули PIO. А так как у меня модуль, что-то делает на частоте полученной после предделителя, то после прихода триггера он ещё может значительное время не выставлять track_count. Поэтому в коде я мониторю 32 бит, и уже там понимаю, когда появились валидные данные. Подключен модуль так:
И вот там где стоит константа 256, нужно было завести значение регистра 0xB, но так как оно приставкой всегда ставилось именно в 256, я решил, что и так сойдёт. После связки всего этого воедино, и теста в аудио плеере. Оказалось, что приставка нормально воспринимает пределитель 720 и выше. Игры тоже нормально грузились. И я начал проходить FF7, неспешно решая, всякие мелкие проблемы. Однако на ледяном уровне которые идёт после сноуборда(там где можно замерзнуть), приставке начало сносить крышу. Она металась лазером туда сюда, и нормально не работала. Глюк был плавающий, то есть нормально повторению не поддавался. В итоге я вывел на клавиатуру подстройку этого предделителя, и побаловавшись подобрал значение 1400 при котором и позиционирование было быстрое, и глюков не вызывало. В общем, в этом месте я получил рабочий эмулятор. Всем спасибо за внимание.
Не совсем. Остались ещё всякие мелочи, которые были в процессе, но хронологию их появления я не помню.
Да вот такой SGDMA бяка. Если началась передача, то остановить её уже не получится, он с головой уходит в работу и слышать ничего не хочет. Есть, конечно, вариант сделать ему ресет через регистр, но вот, что говорит официальная документация:
В общем можно всю шину завесить, и придётся вообще делать полный ресет, всей системе. Казалось бы что плохого. А вот что приставка отдала команду прыгнуть на несколько треков вперед. А у нас после этой команды успевают прилететь субканальные данные последнего посланного трека. Она это видит и понимает, что спозиционировалась не туда. И дает команду ешё прыгнуть, тут прилетает нормальный трек, но команда позиционирования уже улетела, и мы, выполнив ее, снова возвращаем не то, что надо. В общем, такое бывает редко, но могло задержать чтения нужного трека. Чаще она просто лишний раз шлет сигнал остановки привода и всё. Чтобы решить эту маленькую но проблему, я сделал модуль:
Логика работы простая, модуль реализует сигналы шин Avalon-ST выхода и выхода. В обычном состоянии он просто шлёт вход на выход. Если пришёл сигнал drain модуль выставляет флаг lock_drain. И блокирует передачу сигнала out_valid чтобы модули, стоящие за ним, думали что данных нет. При этом сигнал in_ready наоборот рапортует, что готов принимать данные, в итоге данные принимаются, но не уходят. При поступлении сигнала in_sof, lock_drain сбрасывается в ноль и модуль просто начинает работать, посылая копию входа на выход. Управляется всё это опять же через обычный PIO.
Финальная версия SOPC теперь выглядит вот так:
По факту оказалось, что приставка всё равно промахивается меньше при длинных переходах, чем мой код. Почему так происходит, я уже знал. Диск увеличивается от центра к краю, и длинна окружности растет. Данные же записаны с неизменным интервалом. И получается, что в конце диска на один оборот мы имеем больше секторов, чем в начале. Привод, ориентируясь на скорость поступления данных, регулирует скорость вращения диска, поэтому данные с привода, идут потоком равномерным. Но вот прыжок на один трек, в начале диска и в конце дают разный результат. Я где то читал(где уже не помню), что SUP-CPU имеет таблицу коррекции и при помощи неё он рассчитывает на сколько треков прыгнуть, чтобы оказаться в нужном месте. Я даже пытался её найти в прошивке, но надо сказать безуспешно. Тогда я написал письмо Martin'у Korth также известному, как Nocash с просьбой помочь в этом вопросе. Он достаточно быстро ответил, за, что ему огромное спасибо. И прислал нужные таблицы, с кусками ассемблерного кода, и пояснениями как оно все работает. Таблица была устроена следующим образом, в ней около 80 элементов(вообще там несколько таблиц для дисков с разным объемом записанных данных, но из за ошибки все равно используются только первые 71 элемент). И каждый элемент указывает сколько каждая минута звучания(ну и данных по совместительству) занимает треков. То есть элемент номер 4 у нас был 0xDD, значит 3 минута звучания, занимает 221 трек. А 57 элемент 0x6D, то есть 58 минута диска уже всего 109 треков. Приставка рассчитывает по этим данным как далеко переместить каретку. Я видоизменил таблицу, так чтобы мне было с ней удобно работать. И добавил в свой код, после чего позиционирование стало в разы лучше.
Иногда ещё до добавления правильно позиционирования, бывали моменты, когда мы улетали за пределы LeadOut. На SD карте обычно там был уже мусор, или образ другого диска. Поэтому приставку не хило колбасило. И заканчивалось перечитыванием TOC и попыткой повторить чтение данных. Это решилось просто, в заголовоке которые идёт в начале образа SD карты, описаны три параметра, первый это первый сектор образа, второй начало зоны LeadOut, и третий длина образа. Если мы вдруг оказались за пределами образа, я выполняю прыжок на начало зоны LeadOut. Работает отлично.
Насколько я понимаю, основная заморочка это сигнал CDBCLK он должен, быть четко засинхронизирован с приставкой иначе на музыке точно будут щелчки. С остальными всё проще. Думаю идеально найти контролер, который может делать шину I2S с параметрами 24 бита на канал, и 24 бита слово. Либо 16 бит на канал и 24 на слово. STM32 когда я смотрел, вроде бы так не умели. Скорей всего отлично подойдут PSoC 5 и возможно ESP32, но в последних, не уверен. Сигналы управления шиной данных CXD2545 медленные порядка 70 килогерц, шины субканала порядка 133 килогерц, думаю можно ногодрыком реализовать. Ну, или SPI попробовать сконфигурировать для этого. SENS не смотря, что это мультиплексор, в целом приставкой читается как регистр. То есть посылает на шину данных, что именно хочет увидеть, а потом через паузу небольшую читает значения ноги SENS. Так что можно сильно не заморачиваться, и тоже сделать ногодрыком.
В целом да, есть несколько идей:
У всего есть свои проблемы. Пункт первый мало интересен. Так как почти ничего нового не даст. Пункт второй тут очень желателен логический анализатор на 32 канала, в целом там хватит и 20 но у меня сейчас только 15 канальный(один канал умер). И тратится на 32 канала, пока не хочется. Конечно, появится возможность грузить игры по сети, и всё остальное, что может дать полный контроль подсистемы привода, но анализатора пока в наличии нет. А изготовления своего подзаморожено. Третий вариант самый захватывающий. Но он требует реверсить схему платы PU-18 либо PU-8 последний вариант мне больше нравится. И потом разводить свою плату с CXD2545/CX2510 чтобы удобно отлаживать. Идея заняться есть, но пока руки не доходя, особенно до разводки платы. Ну а работа с картой через, SDIO как бы тоже не так уж и интересно.
Вот теперь точно всё. Эмулятор готов. Работоспособность подтверждена практикой. Проект для меня получился очень интересным, хоть и невероятно длинным. Опыта с ним я хлебнул на славу. Но многое из того, с чем столкнулся не однократно пригодилось в жизни. В общем радиоэлектроника и программирование бывают очень интересными. А получить рабочие устройство это не передаваемое чувство радости и гордости. Поэтому если хотели попробовать этим заняться, возможно самое время. Не факт, что получится с первого раза, Но как говорится терпенье и труддо добра не доведут могут быть вознаграждены. Всем спасибо за внимание. Теперь точно конец.
PS. Чуть позже(1-2 недели) весь код(Си и Verilog) проекта будет выложен на гитхабе, мне просто очень хочется его немного «причесать». А затягивать из за этого последнюю часть не хотелось.
PSS. Если вас вдруг заинтересовало как хранятся данные на CD, вы прочитали указанную во второй части книгу. И вам хочется пощупать как же выглядит этот EFM сигнал в жизни(мне однажды очень захотелось). Здесь лежит файл снятого EFM сигнала, и частоты с пина C16M, которая должна меняться в зависимости от скорости данных. Когда то мне удалось вытащить из этого сигнала порядка двух полных секторов.
PSSS. Ну вот теперь уж точно конец.
9.5 Эмуляция SENS
После того как я пришёл к выводу, что SENS это выход мультиплексора. Проблем с его реализацией не возникло. Хотя я очень долго пытался побороть желание всё это запихнуть в модуль CXD2545_CPU уж некоторые части слишком похожие были. Но потом решил, что лучше два маленьких хоть и похожих модуля, чем один огромный.
Реализация SENS
module CXD2545_SENS(
input sclk,
input clk,
input data,
input xlat,
input [15:0] sens_data,
output reg sens
);
reg [3:0] cnt;
reg [7:0] shift_reg;
reg [3:0] select_reg;
reg prev_clk;
reg prev_xlat;
always @(posedge sclk) begin
if((prev_xlat == 1'b1) && (xlat == 1'b0)) begin
cnt <= 0;
shift_reg[7:0] <= 0;
end else begin
if((prev_clk == 1'b0) && (clk == 1'b1)) begin
shift_reg[7:0] <= {data, shift_reg[7:1]};
if(cnt < 7) begin
cnt <= cnt + 1'b1;
end else begin
select_reg <= {data, shift_reg[7:5]};
cnt <= 0;
end
end
end
prev_xlat <= xlat;
prev_clk <= clk;
sens <= sens_data[select_reg];
end
endmodule
Ничего сложного тут нет, всё это я уже описывал и раньше. Только немного уточнений:
Имя | Описание |
sclk | Выход тактовой частоты процессора |
clk | Вход CLOK от CXD2545 |
data | Вход DATA от CXD2545 |
xlat | Вход XLAT от CXD2545 |
sens_data | Вход 16 сигналов |
sens | Выход этого самого SENS |
Чтобы управлять статусом этих пинов в систему был добавлен ещё один модуль PIO, на который и были подключены почти все пины SENS, кроме COUT:
CXD2545_SENS sens_inst(
.sclk(CPU_CLK),
.clk(REG_CLK),
.data(REG_DATA),
.xlat(REG_XLAT),
.sens_data({SENS_PIN[15:13], trc_toggle, SENS_PIN[11:0]}),
.sens(sens_out)
);
На самом деле всё это, наверное, усложнено и есть куда более простой вариант, но меня пугали всякие слова типа метастабильность и синхронный дизайн.
Более простой и возможно правильный вариант
module CXD2545_SENS(
input clk,
input data,
input xlat,
input [15:0] sens_data,
output wire sens
);
reg [3:0] cnt;
reg [7:0] shift_reg;
reg [3:0] select_reg;
always @(posedge clk or negedge xlat) begin
if(xlat == 1'b0) begin
cnt <= 0;
shift_reg[7:0] <= 0;
end else begin
shift_reg[7:0] <= {data, shift_reg[7:1]};
if(cnt < 7) begin
cnt <= cnt + 1'b1;
end else begin
select_reg <= {data, shift_reg[7:5]};
cnt <= 0;
end
end
end
assign sens = sens_data[select_reg];
endmodule
9.5 Эмуляция COUT
Посмотрев снятые логи, я увидел, что приставка смотрит на COUT только когда использует «дальнее позиционирование» при помощи каретки. Поэтому было принято решение. Гнать туда меандр постоянно, но надо было угадать с частотой. Между двумя запросами COUT был промежуток примерно две миллисекунды, это где-то 250 герц максимум, что успеет воспринять приставка в таком темпе. Но это прям оочень максимум. Поэтому сразу сделал так чтобы можно было настраивать его, и по-хорошему наверно имело смысл повесить его на шину AvalonMM но было лень. В итоге родился такой модуль:
CXD2545_TRACK_COUNTER
module CXD2545_TRACK_COUNTER(
input clk,
input [15:0] div,
input [15:0] toggle_cnt,
input trigger,
output reg toggle_clk,
output reg [31:0] track_count
);
reg [32:0] cnt_50;
reg [32:0] cnt_div_50;
reg prev_trigger;
reg [30:0] trc_cnt;
reg trk_clk;
always @(posedge clk) begin
if(cnt_50 < div) begin
cnt_50 <= cnt_50 + 1'b1;
end else begin
cnt_50 <= 0;
trk_clk <= ~trk_clk;
if(cnt_div_50 < 256) begin
cnt_div_50 <= cnt_div_50 + 1'b1;
end else begin
cnt_div_50 <= 0;
toggle_clk <= ~toggle_clk;
end
if((prev_trigger == 1'b1) && (trigger == 1'b0)) begin
track_count <= {1'b1, trc_cnt};
end else if((prev_trigger == 1'b0) && (trigger == 1'b1)) begin
trc_cnt <= 0;
track_count <= 0;
end else begin
trc_cnt <= trc_cnt + 1'b1;
end
prev_trigger <= trigger;
end
end
endmodule
Имя | Описание |
clk | Выход тактовой частоты процессора |
div | Предделитель частоты |
toggle_cnt | Делитель частоты, полученной от предделителя |
trigger | Триггер запуска и остановки счетчика выданых импульсов |
toggle_clk | Выход COUT |
track_count | Число треков которые мы насчитали между сигналами триггера |
CXD2545_TRACK_COUNTER trc_cnt_inst(
.clk(CPU_CLK),
.div(trc_div),
.toggle_cnt(256),
.toggle_clk(trc_toggle),
.trigger(trc_cnt_en),
.track_count(track_count)
);
И вот там где стоит константа 256, нужно было завести значение регистра 0xB, но так как оно приставкой всегда ставилось именно в 256, я решил, что и так сойдёт. После связки всего этого воедино, и теста в аудио плеере. Оказалось, что приставка нормально воспринимает пределитель 720 и выше. Игры тоже нормально грузились. И я начал проходить FF7, неспешно решая, всякие мелкие проблемы. Однако на ледяном уровне которые идёт после сноуборда(там где можно замерзнуть), приставке начало сносить крышу. Она металась лазером туда сюда, и нормально не работала. Глюк был плавающий, то есть нормально повторению не поддавался. В итоге я вывел на клавиатуру подстройку этого предделителя, и побаловавшись подобрал значение 1400 при котором и позиционирование было быстрое, и глюков не вызывало. В общем, в этом месте я получил рабочий эмулятор. Всем спасибо за внимание.
Не совсем. Остались ещё всякие мелочи, которые были в процессе, но хронологию их появления я не помню.
10. Всякие мелочи
10.1 A SGDMA, не умеет прерывать передачу
Да вот такой SGDMA бяка. Если началась передача, то остановить её уже не получится, он с головой уходит в работу и слышать ничего не хочет. Есть, конечно, вариант сделать ему ресет через регистр, но вот, что говорит официальная документация:
Executing a software reset when a DMA transfer is active may result in permanent bus lockup until the next system reset. Hence, Altera recommends that you use the software reset as
your last resort.
В общем можно всю шину завесить, и придётся вообще делать полный ресет, всей системе. Казалось бы что плохого. А вот что приставка отдала команду прыгнуть на несколько треков вперед. А у нас после этой команды успевают прилететь субканальные данные последнего посланного трека. Она это видит и понимает, что спозиционировалась не туда. И дает команду ешё прыгнуть, тут прилетает нормальный трек, но команда позиционирования уже улетела, и мы, выполнив ее, снова возвращаем не то, что надо. В общем, такое бывает редко, но могло задержать чтения нужного трека. Чаще она просто лишний раз шлет сигнал остановки привода и всё. Чтобы решить эту маленькую но проблему, я сделал модуль:
avalon_st_drainer
module avalon_st_drainer (
input wire clk, // clock.clk
input wire reset, // reset_sink.reset_n
output wire out_valid, // out.valid
output wire out_sof, // .startofpacket
output wire out_eof, // .endofpacket
output wire [31:0] out_data, // .data
output wire [1:0] out_empty, // .empty
input wire out_ready, // .ready
input wire in_valid, // in.valid
input wire in_sof, // .startofpacket
input wire in_eof, // .endofpacket
input wire [31:0] in_data, // .data
input wire [1:0] in_empty, // .empty
output wire in_ready, // .ready
input wire drain // conduit_end.export
);
assign out_eof = ((drain == 1'b0) && (lock_drain == 1'b0)) ? in_eof : 1'b0;
assign out_valid = ((drain == 1'b0) && (lock_drain == 1'b0)) ? in_valid : 1'b0;
assign in_ready = ((drain == 1'b0) || (in_sof == 1'b1)) ? out_ready : 1'b1;
assign out_sof = in_sof;
assign out_empty = in_empty;
assign out_data = in_data;
reg lock_drain;
always @(negedge clk) begin
if(drain == 1'b1) begin
lock_drain <= 1'b1;
end else begin
if(in_sof == 1'b1) begin
lock_drain <= 1'b0;
end
end
end
endmodule
Логика работы простая, модуль реализует сигналы шин Avalon-ST выхода и выхода. В обычном состоянии он просто шлёт вход на выход. Если пришёл сигнал drain модуль выставляет флаг lock_drain. И блокирует передачу сигнала out_valid чтобы модули, стоящие за ним, думали что данных нет. При этом сигнал in_ready наоборот рапортует, что готов принимать данные, в итоге данные принимаются, но не уходят. При поступлении сигнала in_sof, lock_drain сбрасывается в ноль и модуль просто начинает работать, посылая копию входа на выход. Управляется всё это опять же через обычный PIO.
Финальная версия SOPC теперь выглядит вот так:
10.2 Позиционирование гораздо хуже чем в оригинале
По факту оказалось, что приставка всё равно промахивается меньше при длинных переходах, чем мой код. Почему так происходит, я уже знал. Диск увеличивается от центра к краю, и длинна окружности растет. Данные же записаны с неизменным интервалом. И получается, что в конце диска на один оборот мы имеем больше секторов, чем в начале. Привод, ориентируясь на скорость поступления данных, регулирует скорость вращения диска, поэтому данные с привода, идут потоком равномерным. Но вот прыжок на один трек, в начале диска и в конце дают разный результат. Я где то читал(где уже не помню), что SUP-CPU имеет таблицу коррекции и при помощи неё он рассчитывает на сколько треков прыгнуть, чтобы оказаться в нужном месте. Я даже пытался её найти в прошивке, но надо сказать безуспешно. Тогда я написал письмо Martin'у Korth также известному, как Nocash с просьбой помочь в этом вопросе. Он достаточно быстро ответил, за, что ему огромное спасибо. И прислал нужные таблицы, с кусками ассемблерного кода, и пояснениями как оно все работает. Таблица была устроена следующим образом, в ней около 80 элементов(вообще там несколько таблиц для дисков с разным объемом записанных данных, но из за ошибки все равно используются только первые 71 элемент). И каждый элемент указывает сколько каждая минута звучания(ну и данных по совместительству) занимает треков. То есть элемент номер 4 у нас был 0xDD, значит 3 минута звучания, занимает 221 трек. А 57 элемент 0x6D, то есть 58 минута диска уже всего 109 треков. Приставка рассчитывает по этим данным как далеко переместить каретку. Я видоизменил таблицу, так чтобы мне было с ней удобно работать. И добавил в свой код, после чего позиционирование стало в разы лучше.
10.3 Вылет за пределы LeadOut
Иногда ещё до добавления правильно позиционирования, бывали моменты, когда мы улетали за пределы LeadOut. На SD карте обычно там был уже мусор, или образ другого диска. Поэтому приставку не хило колбасило. И заканчивалось перечитыванием TOC и попыткой повторить чтение данных. Это решилось просто, в заголовоке которые идёт в начале образа SD карты, описаны три параметра, первый это первый сектор образа, второй начало зоны LeadOut, и третий длина образа. Если мы вдруг оказались за пределами образа, я выполняю прыжок на начало зоны LeadOut. Работает отлично.
11. Ответы на вопросы которые возможно у вас возникнут
Что нужно чтобы сделать такое на микроконтроллере ибо FPGA дорого и сложно?
Насколько я понимаю, основная заморочка это сигнал CDBCLK он должен, быть четко засинхронизирован с приставкой иначе на музыке точно будут щелчки. С остальными всё проще. Думаю идеально найти контролер, который может делать шину I2S с параметрами 24 бита на канал, и 24 бита слово. Либо 16 бит на канал и 24 на слово. STM32 когда я смотрел, вроде бы так не умели. Скорей всего отлично подойдут PSoC 5 и возможно ESP32, но в последних, не уверен. Сигналы управления шиной данных CXD2545 медленные порядка 70 килогерц, шины субканала порядка 133 килогерц, думаю можно ногодрыком реализовать. Ну, или SPI попробовать сконфигурировать для этого. SENS не смотря, что это мультиплексор, в целом приставкой читается как регистр. То есть посылает на шину данных, что именно хочет увидеть, а потом через паузу небольшую читает значения ноги SENS. Так что можно сильно не заморачиваться, и тоже сделать ногодрыком.
Планируются ли ещё работы в этом направлении?
В целом да, есть несколько идей:
- подключится к шине команд SUB-CPU чтобы точно знать куда позиционироваться
- полностью выкинуть SUB-CPU, работать вместо него и CXD2545
- эмулировать работу лазера чтобы можно было подключить к любой PS
- сделать модуль работы с SD картой через SDIO а не SPI
У всего есть свои проблемы. Пункт первый мало интересен. Так как почти ничего нового не даст. Пункт второй тут очень желателен логический анализатор на 32 канала, в целом там хватит и 20 но у меня сейчас только 15 канальный(один канал умер). И тратится на 32 канала, пока не хочется. Конечно, появится возможность грузить игры по сети, и всё остальное, что может дать полный контроль подсистемы привода, но анализатора пока в наличии нет. А изготовления своего подзаморожено. Третий вариант самый захватывающий. Но он требует реверсить схему платы PU-18 либо PU-8 последний вариант мне больше нравится. И потом разводить свою плату с CXD2545/CX2510 чтобы удобно отлаживать. Идея заняться есть, но пока руки не доходя, особенно до разводки платы. Ну а работа с картой через, SDIO как бы тоже не так уж и интересно.
12. Послесловие
Вот теперь точно всё. Эмулятор готов. Работоспособность подтверждена практикой. Проект для меня получился очень интересным, хоть и невероятно длинным. Опыта с ним я хлебнул на славу. Но многое из того, с чем столкнулся не однократно пригодилось в жизни. В общем радиоэлектроника и программирование бывают очень интересными. А получить рабочие устройство это не передаваемое чувство радости и гордости. Поэтому если хотели попробовать этим заняться, возможно самое время. Не факт, что получится с первого раза, Но как говорится терпенье и труд
PS. Чуть позже(1-2 недели) весь код(Си и Verilog) проекта будет выложен на гитхабе, мне просто очень хочется его немного «причесать». А затягивать из за этого последнюю часть не хотелось.
PSS. Если вас вдруг заинтересовало как хранятся данные на CD, вы прочитали указанную во второй части книгу. И вам хочется пощупать как же выглядит этот EFM сигнал в жизни(мне однажды очень захотелось). Здесь лежит файл снятого EFM сигнала, и частоты с пина C16M, которая должна меняться в зависимости от скорости данных. Когда то мне удалось вытащить из этого сигнала порядка двух полных секторов.
PSSS. Ну вот теперь уж точно конец.