PIC单片机精通_A/D转换&异步串口通讯实例与详解

2019-04-15 13:20发布

1.前言

串口通信这个话题对于很多做机电一体化、机器人控制等系统协调的同学,根本不陌生。计算机软件(编程技术),近些年飞速发展。尤其是以机器学习、仿生控制、大数据为代表的AI行业。但是,软件及算法永远不会停留在“数学”这个圈圈里,我们需要把它推向工程,推向实践。这样作为软硬件交流的“握手协议”的重要性就不言自明。 这一片主要介绍比较完整的串口通信代码,具体为PIC16F876a与上位机进行数据交流的事。这个过程包括了模拟信号到数字信号的转换、模拟信号采样率、通信协议、计算机软件、算法设计等一系列有趣的问题。有了硬件的数据采集模块,我们就可以在软件和算法上做更多的创新工作! 本篇主要集中讨论以下问题: 1.定时器初值该如何计算? 2.PIC系列单片机的中断优先级是如何设置的? 3.AD采集模块的数据存储与发送原理是什么? 4.左对齐、右对齐的优势与劣势的缺点又是什么?

2.程序与代码详解

/************************************************** **Author: Shen Chunxu All Rights Reserved! **Tsinghua University 2016-11-20 **ad module + usart module **************************************************/ #include __CONFIG(0x3ffa); #define BAUD 9600 #define FOSC 9216000L #define BaudInitVal ((int)(FOSC/(16UL * BAUD) -1)) //高速情况下设定波特率初始值 #define RX_PIN TRISC7 //定义数据通讯端口 #define TX_PIN TRISC6 unsigned char Rece_flag; //初始化,串口接受标志 unsigned char Time_flag; //初始化,时钟标志 unsigned int adResult; //保存ad转换结果 void InitAdusart(void); //初始化AD模块、串口模块 void InitTimer(void); //定时器1初始化 void main() { INTCON = 0x00; //清空中断控制器 RX_PIN = 1; //端口RX TX初始化 TX_PIN = 0; InitAdusart(); //串口、AD初始化 InitTimer(); //定时器初始化,预装初值 //中断控制 GIE = 1; //允许全局中断 PEIE = 1; //允许外围触发中断 RCIE = 1; //开启串口接收触发中断 TMR1IE = 1; //开启定时器溢出中断 TMR1IF = 0; //定时器溢出标志位初始化 RCIF = 0; //串口接收标志位初始化 TMR1ON = 1; //开启定时器Timer1 while(1) {//'S'对应上位机“开始”按键 if( Rece_flag == 'S') { if( Time_flag == 1) { ADGO=1; //开始ad转换采集 while( ADGO==1 ) continue; //等待转换采集完毕 //adResult = (unsigned int)ADRESH<<8 + ADRESL; // 右对齐10-bit //adResult = (unsigned int)(ADRESH<<6 + ADRESL>>2); //简化运算 10bit->8bit adResult = ADRESH; //////一次串口发送过程 TXREG = adResult; //把AD结果送到发送缓存区 while( TRMT == 0) //等待发送完毕 continue; ///////装入初值,提供足够的采样时间 Time_flag = 0; TMR1L=0xc0; //定时50ms TMR1H=0xc7; } } } } //上位机发送信号触发中断 void interrupt Serve(void) { if( RCIF ) //串口接收中断,优先级最高 { RCIF = 0; //软件清空中断标志位 Rece_flag = RCREG; if('E' == Rece_flag) //对应上位机“结束”按键 { //给PC机反馈信号“END” TXREG = 'E'; while( TRMT == 0 ) continue; //检验发送是否结束 TXREG = 'N'; while(TRMT==0) continue; TXREG = 'D'; while(TRMT==0) continue; } else if( 'S' == Rece_flag ) { //给PC机反馈信号“OK” TXREG = 'O'; while(TRMT==0) continue; TXREG = 'K'; while(TRMT==0) continue; } else //其他信号不响应 { ; } } else if( TMR1IF ) //定时器溢出串口接收中断,优先级次之,主要控制(AD转换时间+串口发送时间) { TMR1IF = 0; //软件清空定时器中断标志位 Time_flag = 1; } } // 串口、AD模块 void InitAdusart(void) { //串口模块初始化 SPBRG = BaudInitVal; //设定波特率 RCSTA=0x90; //1-0-0-1-0-0-0-0=串口工作-8位接收-禁止单字节-连续-空-无帧检错-无溢出检错-无奇偶校验 TXSTA=0x24; //0-0-1-0-0-1-0-0=异步不设置时钟-8位发送-允许发送-异步-空-高速-TSR为空-无奇偶校验 //AD模块初始化 ADCON0=0x49; //01-001-0-0-1=FOSC/16-AN1-不转换-空-上电 //ADCON1=0xc9; //1-1-00-1001=右对齐-FOSC/16-空-VDD+VSS参考电压(AN0~AN5) ADCON1=0x49; //0-1-00-1001=左对齐-FOSC/16-空-VDD+VSS参考电压(AN0~AN5) } //Timer初始化 void InitTimer() { T1CON=0x30; //00-11-0-0-0-0=空-8分频-禁止起振-定时器模式-内部时钟-暂时不开启 TMR1L=0xc0; //定时50ms TMR1H=0xc7; }

3.注意事项

1.PIC系列仅仅提供提供了一个中断服务程序,并没有提供中断优先级寄存器。我们要向实现多级中断有序进行,这里我提供了一种“分支语句+中断标志位”结合的方法,实验证明,该方法确实能够做到中断优先级的管控!这里还是要提示一点,一如中断,首先要清空中断标志寄存器!!!这很重要! 2.关于定时器的初值问题。这里仅仅就PIC16F876a的16位Timer1而言。计算如下:   定时时间(s) = 分频比*(2^16 - 处置)*指令周期 指令周期 = 4*时钟周期     时钟周期 = 1/晶振频率 切记!看好自己的晶振频率在计算。

4.思考与总结

1.利用定时器的精准定时功能,实现周期采样的控制。 2.采用A/D左对齐的方式,使得数据读取更方便。 3.多个分支或者多个条件控制时,提倡采用“设标”的方法进行程序设计!这可以大大提高程序的可读性。在这里,讲一下设标的心得,建议将标志位设为静态变量或者全局变量,这可以大大提高程序的健壮性!