浅谈DFS,BFS,IDFS,A*等算法
这些年下来,家里,公司有很多废旧的电视机,显示器,投影机。你说扔掉吧,有点可惜,卖给收废品的吧,其实和扔也差不多。总想着怎么把这个淘汰下来的显示器给利用上呢。
这些显示器都有个共性,就是带有VGA接口。上网搜索研究了一下,发现VGA接口是可以编程驱动的。
VGA的电气接口除了GND以外,基本的必须有5条信号线:hsync行同步,vsync场同步,red红,green绿,blue蓝。VGA的时序要求是比较严格的,差一点点都无法正常显示。具体的VGA时序,这里就不赘述了,大家可以网上搜索一下。
由于我打算用单片机实现VGA的时序,使用STM32F103测试后可以实现,但由于103的内存太少了,像素的计算搬运有点吃力,最后还是决定使用STM32F4,手头刚好有F401,主频84M,内存64K,足够我使用了。
我这里设计的是一个320*200(横向320个点,纵向200行)的VGA输出音乐频谱模块,基本参数如下:
电源电压:DC5-12V
工作电流:<30mA
频率响应300-18kHz
声道数:2
为了能够稳定输出时序,使用了两个定时器中断分别输出行频和场频。其次需要对音频进行40Khz的高速采样,这里也使用了一个定时器+DMA,最后还需要对音频进行RFFT运算,得到幅值后转换为像素显示。前前后后打了5次PCB,花了将近4个月的时间完成,中间也遇到不少坑,这里只把最后的成果展示一下,作为疫情宅家纪念。
关键代码:
使用TIM1和TIM2分别做为行输出和场输出信号,在行输出中断中,使用GPIOB发送颜色信号,在场消隐中计数,并复位图像显示头部。
强大的 Python 任务自动化工具!invoke 十分钟入门指南
void TIMER_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef nvic; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; u32 TimerPeriod = 0; u16 Channel1Pulse = 0, Channel2Pulse = 0, Channel3Pulse = 0; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_8; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1); GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOB, &GPIO_InitStructure); // RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4); //return; /* Horizontal timing ----------------- */ TimerPeriod = 2048; Channel1Pulse = 144; /* HSYNC */ Channel2Pulse = 352; /* HSYNC + BACK PORCH */ TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = TimerPeriod; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set; TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; TIM_OC2Init(TIM1, &TIM_OCInitStructure); /* TIM1 counter enable and output enable */ TIM_CtrlPWMOutputs(TIM1, ENABLE); /* Select TIM1 as Master */ TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable); TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); /* Vertical timing --------------- */ /* VSYNC (TIM2_CH2) and VSYNC_BACKPORCH (TIM2_CH3) */ /* Channel 2 and 3 Configuration in PWM mode */ TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Gated); TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0); TimerPeriod =600;//625; /* Vertical lines */ Channel2Pulse = 2; /* Sync pulse */ Channel3Pulse = 24; /* Sync pulse + Back porch */ TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period = TimerPeriod; TIM_TimeBaseStructure.TIM_ClockDivision =2; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set; TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; TIM_OC3Init(TIM2, &TIM_OCInitStructure); /* TIM2 counter enable and output enable */ TIM_CtrlPWMOutputs(TIM2, ENABLE); /* Interrupt TIM2 */ nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); TIM_ITConfig(TIM2, TIM_IT_CC3, ENABLE); /* Interrupt TIM1 */ nvic.NVIC_IRQChannel = TIM1_CC_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE); TIM_Cmd(TIM2, ENABLE); TIM_Cmd(TIM1, ENABLE); }
为了提高速度,一开始使用了DMA传输像素数据,但是,太快了,在行中断后,DMA输出的速度,经过多次尝试无法控制在合理的时序内,导致显示器识别的分辨率过高,图像缩成一点点。最后还是放弃了,使用CPU循环将点阵输出。
以下是电路图:
以下是PCB
以下是实物图和效果图
可以当时钟用
视频链接:https://v.youku.com/v_show/id_XNDUzMzAyNDExMg==.html
痞子衡嵌入式:嵌入式里堆栈原理及其纯C实现