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.多个分支或者多个条件控制时,提倡采用“设标”的方法进行程序设计!这可以大大提高程序的可读性。在这里,讲一下设标的心得,建议将标志位设为静态变量或者全局变量,这可以大大提高程序的健壮性!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮