打印
[学习资料]

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

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


使用特权

评论回复
沙发
kxsi|  楼主 | 2021-7-6 12:16 | 只看该作者
2.程序与代码详解

/**************************************************
**Author: Shen Chunxu  All Rights Reserved!
**Tsinghua University 2016-11-20
**ad module + usart module
**************************************************/
#include <pic.h>
__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;       
                        }
                }
        }
}



使用特权

评论回复
板凳
kxsi|  楼主 | 2021-7-6 12:52 | 只看该作者
//上位机发送信号触发中断
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;
        }
}



使用特权

评论回复
地板
kxsi|  楼主 | 2021-7-6 12:53 | 只看该作者
// 串口、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;
}


使用特权

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


使用特权

评论回复
6
kxsi|  楼主 | 2021-7-6 12:54 | 只看该作者
4.思考与总结
1.利用定时器的精准定时功能,实现周期采样的控制。
2.采用A/D左对齐的方式,使得数据读取更方便。
3.多个分支或者多个条件控制时,提倡采用“设标”的方法进行程序设计!这可以大大提高程序的可读性。在这里,讲一下设标的心得,建议将标志位设为静态变量或者全局变量,这可以大大提高程序的健壮性!


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

3309

帖子

2

粉丝