目录
一、前言
二、电路设计
三、程序设计
四、总结
五、参考资料
一、前言
最近闲着没事,搞了个“旋转LED”的小电路板,自己设计的电路板,上面有64个贴片LED排成一排显示,本文要介绍的是用定时器触发+DMA传输的方式在IO口上产生74HC573和74HC238的控制时序,完成循环点亮64个LED的功能。记录下调试的过程。
二、电路设计
用的单片机是STM32F103C8T6,直接用单片机引脚连接每个LED肯定是不够用的,因为也就只有30多个控制引脚,想了一下用了8个74HC573来控制,用PA0到PA7这8个IO统一连接到8个573芯片的输入端,如下图所示:
用一片74HC238芯片来控制8个573芯片的LE脚,如下图,74HC238和74HC138芯片功能是类似的,只不过74HC238的有效输出电平是高电平而已,和74HC138正好相反。
64个LED的连接电路如下:
三、程序设计
LED显示并没有采用while循环使劲刷新LED状态的方法,而是采用定时器触发DMA传输数据到GPIO口来改变GPIO口输出状态的方式来达到刷新LED显示状态的目的,整个时序刷新过程不需要CPU的干预,原理如下:
我们划分出一块连续的RAM内存区域作为“显存”,用定时器来产生固定周期频率的定时器溢出事件,然后将DMA的传输与定时器溢出事件进行绑定,DMA模块就可以循环往复的将“显存”内的数据送往GPIOA,让GPIOA的16个IO引脚根据“显存”输出不同的电平状态,也就产生了特定的控制时序,达到控制外围芯片的目的。
定时器触发DMA传输的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | WORD wLedStateBuf[64 + 8]; void TIM3_Init() { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 开启定时器3时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); TIM_ClearITPendingBit(TIM3,TIM_IT_Update); TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE ); //关闭定时器更新中断 TIM_TimeBaseInitStructure.TIM_Period = 1000; TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1; TIM_TimeBaseInitStructure.TIM_ClockDivision = 0; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); TIM_Cmd(TIM3,ENABLE); /* 设置NVIC参数 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE); //定时器更新事件触发DMA传输 } void DMA1_Init() //DMA初始化 { DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)&GPIOA->ODR;//DMA外设地址 DMA_InitStructure.DMA_MemoryBaseAddr=(u32)wLedStateBuf;//DMA内存地址 DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//外设作为数据传输的来源 DMA_InitStructure.DMA_BufferSize=64 + 8;//指定DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址寄存器递增 DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外设数据宽度16 DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存储数据宽度16 DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//工作在循环缓存模式 DMA_InitStructure.DMA_Priority=DMA_Priority_High;//DMA通道x拥有高优先级 DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输 DMA_Init(DMA1_Channel3,&DMA_InitStructure); //TIM3更新事件在DMA1通道3内 DMA_Cmd(DMA1_Channel3,ENABLE);//使能DMA1通道3 DMA_Cmd(DMA1_Channel3,ENABLE); } |
100ms触发一次DMA传输,具体的DMA通道不是随意选择的,需要查表看定时器3的更新事件映射在DMA1的哪个通道。
LED初始化和main函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | void LED_Init() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* 开启GPIO时钟 */ 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_Pin_8 | GPIO_Pin_9 \ | GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIOA->ODR &= ~((uint16_t) 0x0FFF); } int main() { int i; //给显存赋值,内容为64个LED循环依次点亮 for(i=0; i<64 + 8; i++) { if(i % 9 == 8) { wLedStateBuf[i] = (uint32_t)(i / 9 + 1) << 8; } else { //利用8、9、10引脚选中某个573芯片,0~7引脚操作要亮的LED wLedStateBuf[i] = ((uint16_t)(i / 9) << 8) | ((uint16_t)1 << (i % 9)); } } LED_Init(); TIM3_Init(); DMA1_Init(); while(1) { //处理其他事情 } } |
用这种方式不需要CPU去不断的刷时序了,这些都由DMA去完成了。CPU要做的只是根据需要去改变“显存”里的东西,腾出时间给CPU去处理其他事情。
用逻辑分析仪抓取各个GPIO引脚的时序图如下:
四、总结
定时器触发+DMA传输的方式可以完成很多操作,就如本文所述,用这种方式来产生74HC573和74HC238的控制时序。还有比如我们要产生16路甚至更多路的精确的PWM波时,用定时器产生的话通道数不够而且必须在特定的引脚上才能产生,使用本文所述这种方式同样不需要占用CPU的处理时间而且输出引脚任意选择非常方便。再比如还有定时器+DMA传输+DAC输出的方式可以用来输出各种各样的波形,非常好用。
五、参考资料
《STM32F10xxx中文参考手册_V10》
链接:https://pan.baidu.com/s/14NGfTZkjhRfx9AXFhT8W5Q 提取码:8g4w