趁着现在刚做完这个小项目,脑子里的感觉还新鲜,赶紧写写文章来总结一下,方便日后查看。 本文主要分两大部分来展开,先总结体会心得,再具体说说怎么做 这种思考方式主要是把“是什么”、“怎么用”这两方面解决就行了,先明确我要干嘛,我在干嘛,这在我的上一个小项目里面也用到。 每当调试新的模块,很多时候不是一下子就能成功的,往往遇到问题。 主要是实现三个功能:录指纹、刷指纹和删除指纹 指纹模块所用串口2代码 SIM900A有许多功能,像打电话、接电话、GPRS等等,这里只用到发短信功能。只需要通过串口发送相关AT指令,收到正确的应答即可。 SIM900A所用串口3代码 蜂鸣器主要作用是用来提醒用户操作出错,因为用的是无源蜂鸣器,需要PWM波驱动,这里给出PWM波的代码 步进电机需要ULN2003来驱动,工作原理可看这个 我采用的是半步驱动方式,时序图如下 代码如下 ①供电问题。心得体会
思考方式
比如在学习如何使用串口的时候,我把串口的几个参数搞明白是什么,以及如何用串口发送函数、怎么处理接收中断就行了。在调试AS608、SIM900A的时候,因为这些本身就已经模块化、函数化了,我并不需要了解指纹识别算法,SIM900A是如何发送、接收信息的,我只需要知道你给它什么指令,它就怎么去做,这样就已经能够达到目的了。如何debug
首先是想想自己对这个模块的理解吧,既然要用它,那么这一整套操作流程下来是怎么样的?找出来还有疑问、不清晰的地方,就我目前的体会而言,要多动手,不能让疑惑只留在脑海里,一般在解决疑问的过程就能解决问题。
例如调试SIM900A时,用4节1.5V电池给模块供电,电话打不进去,串口发送指令无响应,并且它上面D5、D6两个LED灯异常长亮,没有用户手册里面说的情况,一开始我也是挺懵的。就梳理了下发送指令的流程,感觉这个应该没什么问题,可能就是模块自身的问题吧,然后发现模块供电必须要求是5V 1A的,四节电池已经6A了,所以就改用三节电池看看,发现LED指示灯的情况符合手册说的供电不足,最后确定问题下来了。具体做法
AS608指纹识别模块
想了解原理的话可以看这个视频(我不是打广告的)
AS608原理讲解
了解原理后,结合用户手册流程图和例程理解,很快就懂了
#include "fingerprint.h" #include "as608.h" #include "led.h" #include "pwm.h" #include "key.h" #include "delay.h" #include "MOTOR.h" u16 ValidN;//模块内有效模板个数 void Add_FR(void) { u8 i=0,unlock=0,ensure,processnum=0; u16 ID; unlock = encryption(); if(unlock == 1) { while(1) { switch(processnum) { case 0: i++; Remind();//提示用户操作,按下指纹 while(PS_Sta != 1);//等待用户按下手指 ensure=PS_GetImage();//等待用户按下,命令指纹模块采集图像至图像缓冲区 if(ensure == 0x00) { ensure=PS_GenChar(CharBuffer1);//生成特征,保存至缓冲区1 if(ensure==0x00) { i=0; //Success();//提示成功了 processnum=1;//跳到第二步 } else Error(); } else Error(); break; case 1: i++; Remind();//提示用户操作,按下指纹 while(PS_Sta != 1);//等待用户按下手指 ensure=PS_GetImage();//命令指纹模块采集图像至图像缓冲区 if(ensure==0x00) { ensure = PS_GenChar(CharBuffer2);//生成特征,保存至缓冲区2 if(ensure == 0x00) { i = 0; processnum=2;//跳到第三步 } else Error(); } else Error(); break; case 2: ensure=PS_Match();//比对缓冲区1和2两个特征是否一致 if(ensure == 0x00) { //Success(); processnum=3;//跳到第四步 } else { Error(); i=0; processnum=0;//跳回第一步 } break; case 3: ensure = PS_RegModel();//将CharBuffer1与CharBuffer2中的特征文件合并生成模块存于CharBuffer1与CharBuffer2 if(ensure == 0x00) { //Success(); processnum=4;//跳到第五步 } else { processnum = 0; Error(); } break; case 4: ensure=PS_ValidTempleteNum(&ValidN);//读取指纹个数 if(ensure != 0x00) { Error(); } // ensure=PS_ReadSysPara(&AS608Para);//读AS608模块参数 // if(ensure != 0x00) // { // Error(); // } // do ID = ValidN;//ID递增 // while(!(ID<300)); ensure = PS_StoreChar(CharBuffer2, ID);//储存模板 if(ensure == 0x00) { Success(); return ;//返回空值,退出函数 } else { processnum=0; Error(); } break; } if(i == 5)//超过5次没有按手指则退出 { Error(); break; } } } else Error(); } void press_FR(void) { u8 i; SearchResult search; u8 ensure; ensure=PS_GetImage(); if(ensure == 0x00)//获取图像成功 { ensure=PS_GenChar(CharBuffer1); if(ensure == 0x00)//生成特征成功 { ensure= PS_HighSpeedSearch(CharBuffer1,0,300,&search); if(ensure == 0x00)//搜索成功 { Success(); Motorcw();//开锁 for(i=0;i<5;i++) delay_ms(1000); Motorccw();//关锁 } else Error(); } else Error(); } } void Del_FR(void) { u8 ensure,unlock = 0; u8 key_num; unlock = encryption(); if(unlock == 1) { Remind();//提示用户操作 do key_num = KEY_Scan(0); while(key_num == 0); if(key_num == KEY0_PRES)//key0清空指纹库 { ensure=PS_Empty();//清空指纹库 if(ensure == 0x00) Success(); else Error(); } if(key_num == KEY1_PRES)//key1删除序号最大的指纹,也就是最新录入的那个 { ensure=PS_ValidTempleteNum(&ValidN);//读取指纹个数 if(ensure == 0x00) { ensure = PS_DeletChar((ValidN-1),1); if(ensure == 0x00) Success(); else Error(); } } } else Error(); }
#include "delay.h" #include "usart2.h" #include "stdarg.h" #include "stdio.h" #include "string.h" #include "timer.h" //USART2_TXD: PA2 //USART2_RXD: PA3 //AS608指纹模块与单片机接线 ///////////////////////////// //Vi接3.3V //Tx接PA3 //Rx接PA2 //GND接GND //WAK接PA6 //Vt接3.3V ///////////////////////////// //串口接收缓存区 u8 USART2_RX_BUF[USART2_MAX_RECV_LEN]; //接收缓冲,最大USART2_MAX_RECV_LEN个字节. u8 USART2_TX_BUF[USART2_MAX_SEND_LEN]; //发送缓冲,最大USART2_MAX_SEND_LEN字节 //通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据. //如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到 //任何数据,则表示此次接收完毕. //接收到的数据状态 //[15]:0,没有接收到数据;1,接收到了一批数据. //[14:0]:接收到的数据长度 vu16 USART2_RX_STA=0; void USART2_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据 { res =USART_ReceiveData(USART2); if((USART2_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据 { if(USART2_RX_STA<USART2_MAX_RECV_LEN) //还可以接收数据 { TIM_SetCounter(TIM5,0);//计数器清空 //计数器清空 if(USART2_RX_STA==0) //使能定时器5的中断 { TIM_Cmd(TIM5,ENABLE);//使能定时器5 } USART2_RX_BUF[USART2_RX_STA++]=res; //记录接收到的值 }else { USART2_RX_STA|=1<<15; //强制标记接收完成 } } } } //初始化IO 串口2 //pclk1:PCLK1时钟频率(Mhz) //bound:波特率 void usart2_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口2时钟使能 USART_DeInit(USART2); //复位串口2 //USART2_TX PA2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2 //USART2_RX PA3 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PA3 USART_InitStructure.USART_BaudRate = bound;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART2, &USART_InitStructure); //初始化串口2 USART_Cmd(USART2, ENABLE); //使能串口 //使能接收中断 USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM5_Int_Init(99,7199); //10ms中断 USART2_RX_STA=0; //清零 TIM_Cmd(TIM5,DISABLE); //关闭定时器5 } //串口2,printf 函数 //确保一次发送数据不超过USART2_MAX_SEND_LEN字节 void u2_printf(char* fmt,...) { u16 i,j; va_list ap; va_start(ap,fmt); vsprintf((char*)USART2_TX_BUF,fmt,ap); va_end(ap); i=strlen((const char*)USART2_TX_BUF); //此次发送数据的长度 for(j=0;j<i;j++) //循环发送数据 { while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕 USART_SendData(USART2,USART2_TX_BUF[j]); } } //串口接收数据时用到的定时器5 //定时器5中断服务程序 void TIM5_IRQHandler(void) { if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)//是更新中断 { USART2_RX_STA|=1<<15; //标记接收完成 TIM_ClearITPendingBit(TIM5, TIM_IT_Update ); //清除TIM5更新中断标志 TIM_Cmd(TIM5, DISABLE); //关闭TIM5 } } //通用定时器5中断初始化 //这里时钟选择为APB1的2倍,而APB1为42M //arr:自动重装值。 //psc:时钟预分频数 //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us. //Ft=定时器工作频率,单位:Mhz //通用定时器中断初始化 //这里始终选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 void TIM5_Int_Init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);//TIM5时钟使能 //定时器TIM5初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE ); //使能指定的TIM5中断,允许更新中断 TIM_Cmd(TIM5,ENABLE);//开启定时器5 NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 }
SIM900A短信模块
发短信需要哪几种AT指令,发送流程是怎么样的,这些都可以在其用户手册里面找到,这里不再赘述了。
以下代码是向目标手机发送英文短信的:#include "sim900a.h" #include "usart3.h" #include "string.h" #include "delay.h" #include "usart.h" u8 status=0; //向sim900A发送命令 //cmd:发送的命令字符串(不需要添加回车了),当cmd<0XFF时,发送数字(比如发送0X1A),大于的时候发送字符串 //ack:期待的应答结果,如果为空,则表示不需要等待应答 //waittime:等待时间(单位:10ms) //返回值:0,发送成功(得到了期待的应答结果) // 1,发送失败 u8 sim900a_send_cmd(u8 *cmd, u8 *ack, u16 waittime) { u8 res = 0; USART3_RX_STA=0; if((u32)cmd<=0XFF) { while(DMA1_Channel2->CNDTR!=0);//等待通道2传输完成 USART3->DR=(u32)cmd; } else u3_printf("%srn", cmd);//向串口发送命令 if(ack&&waittime)//如果需要等待应答 { while(--waittime)//等待倒计时 { delay_ms(10); if(USART3_RX_STA&0X8000)//接收到期待的应答结果 { if(sim900a_check_cmd(ack))//检查所接受到的应答是否没期待值 break; USART3_RX_STA=0; } } if(waittime==0) res = 1; } return res; } //sim900a发送命令后,检测接收到的应答 //str:期待的应答结果 //返回值:0,没有得到期待的应答结果 // 其他,期待应答结果的位置(str的位置) u8* sim900a_check_cmd(u8 *str) { char *strx = 0; if(USART3_RX_STA&0X8000)//接收到一次数据了 { USART3_RX_BUF[USART3_RX_STA&0X7FFF] = 0;//添加结束符 strx = strstr((const char*)USART3_RX_BUF, (const char*)str);//strstr这个函数是用来找USART3_RX_BUF里面有没有str这个字符串 } return (u8*)strx; } //1:发送AT指令出错 //2:SIM卡出错 //3:查询不到运营商 u8 sim900a_check_status() { if(sim900a_send_cmd("AT+CPIN?","OK",200)) return 2; if(sim900a_send_cmd("AT+CGMI","OK",200)) return 3; return 0; } u8 sim900a_sms_test(u8* msisdn)//输入参数为手机号码 { //在进入这个函数之前,需要事先发送"AT"同步波特率,并接受到"OK" char cmd[20]; status=sim900a_check_status(); if(status) return status; if(sim900a_send_cmd("AT+CMGF=1","OK",200)) return 4;//设置文本模式 if(sim900a_send_cmd("AT+CSCS="GSM"","OK",200)) return 5;//设置TE字符集为GSM sprintf((char*)cmd, "AT+CMGS="%s"", msisdn);//命令格式:AT+CMGS='xxx' if(sim900a_send_cmd((u8*)cmd, ">",200)) return 6;//设置短信息文本模式参数 u3_printf("%s", "dangerous "); if(sim900a_send_cmd((u8*)0X1A, "+CMGS:", 1000)) return 7;//发送结束符 return 0; }
#include "delay.h" #include "usart3.h" #include "stdarg.h" #include "stdio.h" #include "string.h" //USART3_TXD: PB10 //USART3_RXD: PB11 //与SIM900A引脚连接 ///////////////////////////////// //GND接GND,共地 Vcc.mcu接5V或者3.3V,为串口TTL电平大小 //如果Vcc.mcu接5V,PB10接5VR,PB11接5VT //如果Vcc.mcu接3.3V,PB10接3VR,PB11接3VT ///////////////////////////////// //串口接收缓存区 u8 USART3_RX_BUF[USART3_MAX_RECV_LEN];//接收缓冲,最大USART3_MAX_RECV_LEN个字节 u8 USART3_TX_BUF[USART3_MAX_SEND_LEN];//发送缓冲,最大USART3_MAX_SEND_LEN个字节 //判断接收的两个字符之间的时间差是否大于10ms来判断是不是一次连续的数据 //如何两个字符间隔大于10ms,则判断不是1次连续的数据 //相当于自定义了通信协议 u16 USART3_RX_STA=0; void USART3_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到一个字节产生中断 { res = USART_ReceiveData(USART3); if((USART3_RX_STA&(1<<15))==0) { if(USART3_RX_STA<USART3_MAX_RECV_LEN)//还可以接收数据 { TIM_SetCounter(TIM4, 0);//计数器清空 if(USART3_RX_STA == 0) TIM4_Set(1);//从0开始接收到第一个数,启动TIM4判断间隔 USART3_RX_BUF[USART3_RX_STA]= res; //记录接收到的值 USART3_RX_STA++; } else { USART3_RX_STA |= 1<<15;//强制标记接收完成,因为已经超出存储范围 } } } } //初始化IO 串口3 //pclk1:PCLK1时钟频率(Mhz) //bound:波特率 void usart3_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口3时钟使能 USART_DeInit(USART3); //复位串口3 //USART3_TX PB10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10 //USART3_RX PB11 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11 USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口 3 USART_Cmd(USART3, ENABLE); //使能串口 USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE); //使能串口2的DMA发送 UART_DMA_Config(DMA1_Channel2,(u32)&USART3->DR,(u32)USART3_TX_BUF);//DMA1通道7,外设为串口2,存储器为USART2_TX_BUF //使能接收中断 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM4_Init(99,7199);//10ms中断 USART3_RX_STA=0; //清零 TIM4_Set(0);//关闭定时器4 } //串口3的printf 函数 //确保一次发送数据不超过USART3_MAX_SEND_LEN字节 void u3_printf(char* fmt,...) { va_list ap; va_start(ap,fmt); vsprintf((char*)USART3_TX_BUF, fmt, ap); va_end(ap); // i = strlen((const char*)USART3_TX_BUF);//此次发送数据的长度 // for(j=0; j<i; j++) // { // while(USART_GetFlagStatus(USART3, USART_FLAG_TC)== RESET);//循环发送,直到发送完毕 // USART_SendData(USART3, USART3_TX_BUF[j]);//把格式化字符串从开发板串口送出去 // } while(DMA1_Channel2->CNDTR!=0);//等待通道2传输完成 UART_DMA_Enable(DMA1_Channel2, strlen((const char*)USART3_TX_BUF));//通过DMA发送出去 } //定时器4中断服务程序 void TIM4_IRQHandler(void) { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)//是更新中断 { USART3_RX_STA|=1<<15; //标记接收完成 TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIMx更新中断标志 TIM4_Set(0); //关闭TIM4 } } //设置TIM4的开关 //sta:0,关闭;1,开启; void TIM4_Set(u8 sta) { if(sta) { TIM_SetCounter(TIM4,0);//计数器清空 TIM_Cmd(TIM4, ENABLE); //使能TIMx } else TIM_Cmd(TIM4, DISABLE);//关闭定时器4 } //通用定时器中断初始化 //这里始终选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 void TIM4_Init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能//TIM4时钟使能 //定时器TIM3初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM4中断,允许更新中断 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 } //DMA1的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/8位数据宽度/存储器增量模式 //DMA_CHx:DMA通道CHx //cpar:外设地址 //cmar:存储器地址 void UART_DMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设 DMA_InitStructure.DMA_BufferSize = 0; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA_CHx, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器 } //开启一次DMA传输 void UART_DMA_Enable(DMA_Channel_TypeDef*DMA_CHx,u16 len) { DMA_Cmd(DMA_CHx, DISABLE ); //关闭 指示的通道 DMA_SetCurrDataCounter(DMA_CHx,len);//DMA通道的DMA缓存的大小 DMA_Cmd(DMA_CHx, ENABLE); //开启DMA传输 }
蜂鸣器模块
//无源蜂鸣器模块 //IO口接PB5 //VCC接5V或者3.3V //GND接地 #include "pwm.h" #include "usart.h" #include "sys.h" #include "stm32f10x_tim.h" #include "delay.h" //通用定时器3中断初始化 //这里时钟选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 //这里使用的是定时器3! void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIMx外设 } //定时器3中断服务程序 void TIM3_IRQHandler(void) //TIM3中断 { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 { TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源 } } //TIM3 PWM部分初始化 //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟 GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO //初始化TIM3 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 //初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC2 TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器 TIM_Cmd(TIM3, ENABLE); //使能TIM3 } void Error(void)//提醒用户出错 { TIM_SetCompare2(TIM3, 9000); delay_ms(1000); TIM_SetCompare2(TIM3, 0); }
步进电机28BYJ-48
步进电机原理
原理博文
A,B,A‘,B’分别代表IN1,IN2,IN3,IN4,也就是单片机的四个引脚,正转就是从左到右按时序图来写程序就行,反转就是从右往左来看。
我采用的是比较直观的方法——直接设置引脚的高低电平,其实也可以通过PWM波来实现,把周期计算好就行。#include "sys.h" #include "MOTOR.h" #include "delay.h" //只控制电机正反转即可 //引脚连接 //IN1:PC3 IN2:PC2 IN3:PC0 IN4:PC13 //步进电机初始化 void Motor_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = IN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = IN2; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin =IN3; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin =IN4; GPIO_Init(GPIOC, &GPIO_InitStructure); Motor_Stop(); } void Motor_Start(void) { GPIO_SetBits(GPIOC, IN1); delay_ms(1); } void Motor_Stop(void) { GPIO_ResetBits(GPIOC, IN1); GPIO_ResetBits(GPIOC, IN2); GPIO_ResetBits(GPIOC, IN3); GPIO_ResetBits(GPIOC, IN4); } //步进电机正转函数 void Motorcw(void) { u8 i; Motor_Start(); for(i=0;i<130;i++) { GPIO_SetBits(GPIOC, IN2); delay_ms(1); GPIO_ResetBits(GPIOC, IN1); delay_ms(1); GPIO_SetBits(GPIOC, IN3); delay_ms(1); GPIO_ResetBits(GPIOC, IN2); delay_ms(1); GPIO_SetBits(GPIOC, IN4); delay_ms(1); GPIO_ResetBits(GPIOC, IN3); delay_ms(1); GPIO_SetBits(GPIOC, IN1); delay_ms(1); GPIO_ResetBits(GPIOC, IN4); delay_ms(1); } Motor_Stop(); } void Motorccw(void) { u8 i; Motor_Start(); for(i=0;i<130;i++) { GPIO_SetBits(GPIOC, IN4); delay_ms(1); GPIO_ResetBits(GPIOC, IN1); delay_ms(1); GPIO_SetBits(GPIOC, IN3); delay_ms(1); GPIO_ResetBits(GPIOC, IN4); delay_ms(1); GPIO_SetBits(GPIOC, IN2); delay_ms(1); GPIO_ResetBits(GPIOC, IN3); delay_ms(1); GPIO_SetBits(GPIOC, IN1); delay_ms(1); GPIO_ResetBits(GPIOC, IN2); delay_ms(1); } Motor_Stop(); }
注意事项
SIM900A要求是DC5V 1A的电源,供电电压不能高于5V,我一开始用的就是6V供电,结果两盏LED指示灯异常长亮, 没有用户手册说的情况,找不到原因;供电电压低于5V的话就会出现供电不足的情况,D5反复长亮几秒灭一秒 ,D6 亮一秒灭一秒(快闪) 。后来采用5V 1A的电源适配器+转接线供电,注意与单片机共地,这样就成功了。
ULN2003模块不能用单片机上的5V电源供电,供电电压范围是5V-12V,电流要求较大,我采用的是4节AA电池总计6V,注意与单片机共地。
②驱动步进电机,各个引脚的状态持续时间要注意。
因为步进电机的原理是通电产生磁性吸引转子转动,如果每两个引脚之间的状态延时时间过短(结合上面代码),反应时间过短,通电线圈产生磁性的时间太短,转子没有完全到位,这样电机就会只抖动而不转动。
状态延续时间如果过长,因为步进角是很小的,在一次转动过程中,转子完全被吸引到相应位置后还要等待一段时间,转得非常慢。因此要调试好合适的延时时间,这里我采用的是1ms延时。注意,如果用的是原子例程里面的delay_ms()函数,对72MHz条件下,nms<=1864,也就是说最大延时为1.864秒。
③SIM900A卡座问题
如果用的是手机中的SIM卡,需要搭配卡套使用。
另外在调试过程中,D5长亮,D6亮一秒灭一秒(快闪) ,模块始终在搜寻网络,发送”AT”指令有回应,说明能同步波特率,但是打电话进去关机,发送”AT+CPIN?”查询模块是否检测到手机卡指令,收不到正确应答。确认问题在模块上面,唯一能动手检查的就是卡座,其他像SIM900A坏掉的情况都无法检查,因此就往这个方向去找问题了。解决方法:
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算