如果对相位抖动要求高,不适合用MCU来做。
CPLD才是更合适的。
最近要研制一个高速扫描系统,用于大脑神经元电压探针ASAP5高速成像,需要用到10MHz时序脉冲信号,首先想到单片机,比用数字电路芯片要灵活啊。比如RP2040就有强大的PIO,实现几十MHz的时序脉冲都不成问题。不过看起来PIO坑还是有点多,暂时还顾不过来。后来测试了Ardunio IDE下编程,直接写数字口,用标准的digitalWrite,只能达到1MHz多一点。
后来参考网上资料,尝试改用gpio_put写数字口,发现速度超快,数字口输出脉冲可达100MHz(使用for循环超过20MHz),如果设置超频时钟,频率能更高。该方案实现起来很简单,几乎不增加软件学习成本,RP2040价格可以低至几元一片,能灵活产生高达几十MHz的简单高频信号,或复杂一些的时序脉冲信号。
下面是一个例程,在Ardunio IDE里面直接编译上传就可以,单片机连电脑的说明见我以前的帖子。程序设置2口为中断口,每收到一个正脉冲就触发4、5、6口产生16.67MHz脉冲,数字口7的gpio_put是拿来耗时间调整频率的,也可以用其他代码来调延时。较长延时则可以用标准delayMicroseconds、sleep等函数。例程中使用了for循环,速度会降低,没有for循环的情况下直接反复设置高低电平才能达到最高速度,例程中串口显示是调试用的,真正使用时要关掉。
后期改进代码使用多通道设置函数,如gpio_init_mask、gpio_put_all,可以达到多通道信号完全同步。实测发现,脉冲时序精度还是很高的,没有明显跳动,但是中断响应需要1us时间,且有4-5ns的跳动。要实现对外部脉冲的高速响应,可能就要用到PIO编程了。
#define delay_8ns __asm__("nop");
#define delay_16ns delay_8ns delay_8ns
long i;
void gpio2_callback(uint gpio, uint32_t events)
{
Serial.println("Test");
for(i=0;i<10000000;i++)
{
gpio_put(4, 1);
gpio_put(5, 1);
gpio_put(6, 1);
gpio_put(7, 1);
//delay_8ns;
gpio_put(4, 0);
gpio_put(5, 0);
gpio_put(6, 0);
//gpio_put(7, 0);
//delay_8ns;
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
gpio_init(4);
gpio_set_dir(4, GPIO_OUT);
gpio_put(4, 0);
gpio_init(5);
gpio_set_dir(5, GPIO_OUT);
gpio_put(5, 0);
gpio_init(6);
gpio_set_dir(6, GPIO_OUT);
gpio_put(6, 0);
gpio_init(7);
gpio_set_dir(7, GPIO_OUT);
gpio_put(7, 0);
gpio_init(2);
gpio_set_dir(2, GPIO_IN);
gpio_set_function(2, GPIO_FUNC_SIO);
gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE, true, &gpio2_callback);
sleep_ms(10);
}
void loop() {
// put your main code here, to run repeatedly:
}
优化了一下代码,多通道同步设置:
#define delay_8ns __asm__("nop");
#define delay_40ns delay_8ns delay_8ns delay_8ns delay_8ns delay_8ns
#define delay_80ns delay_40ns delay_40ns
long i;
void gpio2_callback(uint gpio, uint32_t events)
{
gpio_put_all(255); //all pins to 1: 11111111, can be 3(00000011) to set pin 0 and 1 to 1
gpio_put_all(0); //all pins to 0: 00000000
gpio_put_all(255);
gpio_put_all(0);
delay_8ns;
gpio_put_all(16); //set pin 4 to 1, others to 0: 00010000
gpio_put_all(64); //set pin 6 to 1, others to 0: 01000000
gpio_put_all(16);
gpio_put_all(0); //can add more circles
for(i=0;i<1000;i++) // for loop is slower
{
gpio_put_all(255);
gpio_put_all(0);
}
}
void setup() {
gpio_init_mask (255); //pin 0-7 init: 11111111
gpio_set_dir_all_bits(255-4); //pin 0-7 to GPIO_OUT: 11111111, -4 means pin 2 to GPIO_IN
gpio_pull_down(2);
gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE, true, &gpio2_callback);
sleep_ms(10);
}
void loop() {
}
产品化的共振扫描成像用FPGA,CPLD大概也行。这种共振扫描镜成像需要一些实时控制,它的扫描频率经常有一点漂移,要自动锁定行同步信号,奇偶行扫描也偶尔需要看着输出的图像来手动对齐一下,还要控制场扫描Y振镜,到达一定行数再控制Y振镜回扫,还是需要编程和通信的,比如奇偶行对齐可以用个旋钮加AD采集,也可以电脑上设置然后串口通信。单片机能实现就最简单了,要是不嫌麻烦的话FPGA肯定是最强了,万能钥匙!RP2040主循环空的情况下,实测相位是没什么抖动的,程序都放在中断调用函数里,行扫描线调头的空闲期间才运行。需要注意的是,多口输出的情况下相互是没有完全对齐的。
不算是坑吧,主要是学习成本,还没来得及认真学这个。PIO需要专门编程,用MicroPython好像最容易,GPIO则是指的我现在用的这个,可直接Ardunio下使用代码