重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
TI=0;
创新互联建站专业提供成都主机托管四川主机托管成都服务器托管四川服务器托管,支持按月付款!我们的承诺:贵族品质、平民价格,机房位于中国电信/网通/移动机房,成都联通服务器托管服务有保障!
当SBUF=~0X81;启动发送,当发送完成时,由硬件置位TI,即TI就变成1了,表示表示发送完成,自然就可以退出while(TI==0);循环,不会死循环的
这个问题很简单,用485通讯的话,思路如下
思路:
PC方面:可以用MSCOMM控件先发一个字符表示接收地址,后延迟1ms,(注意PC端在485通讯在字符发送过程中一定要加延迟,这是我多次测试的总结,如果是用调试助手的话,他内部代码已经加过延迟了,就不必考虑这个问题)再发控制指令,初学者建议直接用调试助手
单片机方面:首先对接收数据进行核对,如果不是本地地址,放弃,如果是本地地址,在检测命令是否正确,如果正确,做出处理后返回PC本地地址并发送命令
我举个例子教你怎么玩多站通讯,下面是我已经通过测试的一个程序
#include reg51.h //选用晶振11.0592MHz
#include absacc.h
#define DATA51 DBYTE[0x80] //80H存数据
#define AddressID 0x31 //本机地址1
sbit RS485E=P3^7; //定义485的使能脚// RS5485E=0为接收状态 RS5485E=1为发送状态
sbit MAX485_DIR=P3^7;
main()
{
//****************通讯设置
SCON = 0xF0; //REN=1允许串行接受状态,串口工作模式3,SM2=1
TMOD|= 0x20; //定时器工作方式2
PCON|= 0x80; //波特率提高一倍
IP=0x10; //串口优先级高
// TH1 = 0xFD; //baud*2 /* reload value 19200、数据位8、停止位1。效验位无(11.0592)
TH1 = 0xf4; //fa // //baud*2 /* 波特率4800、数据位8、停止位1。效验位无 (11.0592M)
TL1 = 0xf4;
TR1 = 1; //开启定时器1
EA = 1; // 开总中断
ES = 1; //开串口中断
RS485E=0; // RS5485E=0为接收状态 RS5485E=1为发送状态
while(1);
}
void counter4(void) interrupt 4 using 2 //串口中断
{
while(RI==0);
RI=0;
if(SBUF==AddressID)
{
while(RI==0);
RI=0;
if(SBUF==0x01) //发送指令
{
MAX485_DIR=1; //开发送
SBUF=AddressID;
while(TI==0);
TI=0;
SBUF=0x6f; //发送o
while(TI==0);
TI=0;
SBUF=0x6b; //发送k
MAX485_DIR=0; //开接收
}
if(SBUF==0x00) //接收
{
//这里怎么处理就看你自己要怎么做了,你没要求,我也不好怎么写,就自己写吧
}
}
}
程序调试通过,可以直接套用
就像平时使用串口收发数据一样,没有多大差别!485只是一个硬件规范,与软件无关。在实际编程时只是要考虑使用的设么器件,在收发数据时是否要设定数据方向(如MAX485),或者不需要(MAX488)。
/*
* MEGA8_485_EEPROM_817_ADC_WDT.c
*
* Created: 2013-8-8 16:52:15
* Author: Administrator
*/
#define F_CPU 4000000UL
#include avr/io.h
#include util/delay.h
#include avr/interrupt.h
#include string.h
#include avr/wdt.h //看门狗相关头文件
#include avr/eeprom.h
#define INT8U unsigned char
#define INT16U unsigned int
#define INT32U unsigned long
#define BT_NO 0xDE //电池的识别号码
INT8U eepromx EEMEM;
//全局变量
INT16U ADC_bt_v;
INT8U UART_Send_flag;
INT8U ADC_Buffer[]={0,0,0,0};
static INT32U ADC_bt_Total=0;
INT16U ADC_bt_S;
INT16U ADC_Ref_Buffer[]={0,0};
float ADC_Ref;
INT16U n;
//串口初始化
void Usart_Init(INT16U BAUD)
{
UCSRB =_BV(RXEN)|_BV(TXEN)|_BV(RXCIE); //接收与发送使能,接收与接收结束中断使能
UCSRC=_BV(UCSZ1)|_BV(UCSZ0)|_BV(UPM1)|_BV(URSEL); //8位数据位、1位停止位、偶校验
UBRRL=(F_CPU/BAUD/16-1)%256; //设置波特率
UBRRH=(F_CPU/BAUD/16-1)/256;
}
//单字符发送
void Send_Char(char c)
{
while(!(UCSRA_BV(UDRE))); //判断数据寄存器是否为空
UDR=c;
while(!(UCSRA_BV(TXC))); //判断发送是否结束
UCSRA|=_BV(TXC); //通过置位进行手动清零
}
//串口接收部分
//#define RX_BUFFER_SIZE 2
//uchar rx_buffer[RX_BUFFER_SIZE];
//uchar rx_counter;
//uchar Uart_RecvFlag;
//系统初始化
void System_Init()
{
ADCSRA=0xE6; //10位ADC转换置位,启动转换,64分频
DDRB|=_BV(PB0); //485的收发模式控制端口,低电平为接收模式,高电平为发送模式
PORTB=~_BV(PB0);
DDRC=~_BV(PC0); //ADC0引脚定义
PORTC=~_BV(PC0);
TCCR1B=0x03; //T1预设分频:256
TCNT1=65536-F_CPU/64.0*1.5; //晶振4MHZ,1.5S定时初值
TIMSK=0x04; //允许T1定时器溢出中断
wdt_enable(WDTO_2S); //启动看门狗(溢出时间1.9S,约等于2.0S)
/* WDTCSR=0x0F;*/
DDRD=~_BV(PD2); //INT0中断引脚定义
PORTD|=_BV(PD2);
MCUCR=0x00; //INT0中断低电平触发
GICR=0XC0; //INT0中断使能
DDRD=~_BV(PD3); //INT1中断引脚定义
PORTD|=_BV(PD3);
// EICRA=0x00; //INT1中断低电平触发
eeprom_busy_wait();
ADC_Ref_Buffer[0]=(INT16U)eeprom_read_byte(1);
eeprom_busy_wait();
ADC_Ref_Buffer[1]=(INT16U)eeprom_read_byte(2);
ADC_Ref=(float)(ADC_Ref_Buffer[0]*100.0+ADC_Ref_Buffer[1]);
DDRC|=_BV(PC4); //测试引脚
PORTC|=_BV(PC4);
sei(); //开总中断
}
//------------------------------------------------------------
//对通道CH进行模数转换
//------------------------------------------------------------
INT16U ADC_Convert(void)
{
INT16U Result;
ADMUX=0xC0; //ADC0通道,使用内部参考电压1.1V
Result=(INT16U)((ADCL+(ADCH8))*ADC_Ref/1024.0*83.0/68.0+ADC_Ref);
return Result;
}
INT16U ADC_T()
{
INT16U ADC_bt;
ADC_bt=ADC_Convert();
ADCSRA=0xE6;
_delay_us(500); //延时后重新再读取ADC,关键点
ADC_bt=ADC_Convert();
ADCSRA=0xE6;
return ADC_bt;
}
int main(void)
{
Usart_Init(9600);
System_Init();
_delay_ms(1000);
while(1)
{
_delay_ms(10);
}
}
//--------------------------
//外部中断1中断函数
//--------------------------
ISR(INT1_vect)
{
PORTC=~_BV(PC4);
TIMSK=0X00;
}
//--------------------------
//INT0中断函数,用来校正参考电压
//--------------------------
ISR(INT0_vect)
{
//校正参考电压,要用标准2.200V电压来校正
INT16U ADC_C;
ADCSRA=0xE6; //10位ADC转换置位,启动转换,64分频
ADMUX=0xC0; //ADC0通道,使用内部参考电压1.1V
ADC_C=(INT16U)(ADCL+(ADCH8));
ADC_Ref=4035.0/(ADC_C/1024.0*83.0/68.0+1); //要用标准2.200V来校正,精度要尽量高
ADC_Ref_Buffer[0]=ADC_Ref/100;
ADC_Ref_Buffer[1]=(INT16U)ADC_Ref%100;
eeprom_busy_wait();
eeprom_write_byte(1,ADC_Ref_Buffer[0]);
eeprom_busy_wait();
eeprom_write_byte(2,ADC_Ref_Buffer[1]);
}
//-------------------------------
//定时器1中断程序负责喂狗(1.9S以内)
//-------------------------------
ISR(TIMER1_OVF_vect)
{
TCNT1=65536-F_CPU/64.0*1.5; //重装初值
wdt_reset(); //看门狗复位
}
//USART串口通信接收中断函数
ISR(USART_RXC_vect)
{
INT8U data;
data=UDR;
sei(); //开总中断,实现中断嵌套
UCSRB=~_BV(RXCIE);
switch (data)
{
case BT_NO:
for (INT8U n;n8;n++)
{
ADC_bt_S=ADC_T();
ADC_bt_Total=(INT32U)(ADC_bt_Total+ADC_bt_S);
}
ADC_bt_v=(INT16U)(ADC_bt_Total/8.0);
ADC_bt_Total=0;
ADC_Buffer[0]=ADC_bt_v/1000;
ADC_Buffer[1]=ADC_bt_v/100%10;
ADC_Buffer[2]=ADC_bt_v/10%10;
ADC_Buffer[3]=ADC_bt_v%10;
PORTB|=_BV(PB0);
_delay_ms(1);
Send_Char(BT_NO); //表明哪个电池分送的信号
_delay_ms(1);
PORTB=~_BV(PB0);
PORTB|=_BV(PB0);
_delay_ms(1);
Send_Char(ADC_Ref_Buffer[0]); //MCU参考电压高位
_delay_ms(1);
PORTB=~_BV(PB0);
PORTB|=_BV(PB0);
_delay_ms(1);
Send_Char(ADC_Ref_Buffer[1]); //MCU参考电压低位
_delay_ms(1);
PORTB=~_BV(PB0);
for (INT8U i=0;i4;i++)
{
PORTB|=_BV(PB0);
_delay_ms(1);
Send_Char(ADC_Buffer[i]);
_delay_ms(1);
PORTB=~_BV(PB0);
}
break;
}
UCSRB|=_BV(RXCIE);
}
无论是232还是485通讯协议,单片机中都是使用串口通讯实现,唯一不同的是,由于485是半双工通讯,485通讯中要有方向控制。