问题遇到的现象和发生背景
使用C8051F020实现ADC读取采样计算后使用串口连续发送,串口发送间隔在20ms左右,但是每发送4K数据就有300ms的延迟。
问题相关代码,请勿粘贴截图
#include <c8051f020.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
//宏声明
#define uchar unsigned char //定义uchar类型
#define uint unsigned int //定义uint类型
#define BAUDRATE 115200 //定义串口波特率
#define SAR_CLK 2500000 //定义SAR时钟熟读
#define SAMPLE_RATE 50000 //定义12位数模芯片采样率
#define SYSTEMCLOCK (22118400L) //定义外部晶振速率
#define UART_SIZE 128 //定义串口1读取数据位数
#define SAMP_NUM 22 //定义ADC数组采样数量
#define OPERATING -((SYSTEMCLOCK / SAMPLE_RATE) * SAMP_NUM) //定义ADC运算转换频率
//16位寄存器声明
sfr16 ADC0 = 0xbe; //12位数模芯片数据定义
sfr16 TMR2 = 0xcc; //定时器/计数器2初值
sfr16 RCAP2 = 0xca; //定时器/计数器2重载初值
sfr16 TMR3 = 0x94; //定时器/计数器3初值
sfr16 RCAP3 = 0x92; //定时器/计数器3重载初值
sfr16 TMR4 = 0xF4; //定时器/计数器4初值
sfr16 RCAP4 = 0xE4; //定时器/计数器4重载初值
//引脚位声明
sbit Auto_LED_RED = P2^0; //Auto红灯控制位
sbit Auto_LED_GREEN = P2^1; //Auto绿灯控制位
sbit ACK_LED_GREEN = P3^1; //ACK绿灯控制位
sbit ACK_LED_RED = P3^2; //ACK红灯控制位
sbit Watchdog = P3^3; //3.3V电源看门狗控制位
sbit Manual_LED_RED = P3^6; //Manual红灯控制位
sbit Manual_LED_GREEN = P3^7; //Manual绿灯控制位
//函数声明
void OSCILLATOR_Init (void); //晶振初始化,切换为外部晶振
void PORT_Init (void); //单片机端口初始化
void Ext_Interrupt_Init (void); //外部中断初始化
void Flash_WRITE(void); //非易失128Bety Flash写入
void Flash_READ(void); //读取非易失Flash数据
void ReadData(void); //读取非易失区数据
void ReadData1(void); //非中断读取非易失区数据
void UART1_Init (void); //初始化串口1
void delay(uint num); //延迟函数
void TIMER2_Init(); //初始化定时计数器2,设置功率轮询输出
void TIMER3_Init (int counts); //初始化定时计数器3,设置数模芯片转换速率
void TIMER4_Init (); //初始化定时计数器4,监控串口接受数据间隔
void ADC0_Init (void); //初始化12位数模转换器
void UART1_Deal(void); //串口数据处理
void AcceptReset(uchar output, uchar outarr[]); //串口数据清零
void AcceptReset1(uchar output, uchar outarr[]); //串口数据清零
//全局变量声明
uint idata AckBlink = 0; //显示频率
uint idata SampNum = 0; //采样循环次数
uint idata Samp = 0; //采样数据
long idata SampArr = 0; //存放累积采样数据
long idata SampArrAll= 0; //保存组采样值
float xdata Kvaule = 0; //ADC斜率
float xdata Bvalue = 0; //ADC最小值
float xdata Power = 0; //光功率
uchar idata Byte = 0; //串口1读一位数据
uchar idata InputCheck = 0; //串口1输入累加校验和
uchar idata OutputCheck = 0; //串口1输出累加校验和
uchar idata UART_Input_Size= 0; //串口1输入数据计数
uchar idata UART_Input_First= 0; //串口1输入字符位数
uchar xdata UART_Input[UART_SIZE]; //串口1读取数据大小
uchar idata UART_output_Size = 0; //串口1输出数据计数
uchar idata UART_output_First= 0; //串口1输出字符位数
uchar xdata UART_output[UART_SIZE]; //串口1读出数据大小
uchar xdata Permanent_data[128]; //不丢失数据
uchar code outok[] = {0xEF, 0x00, 0x4F, 0x3E}; //正确返回值
uchar code outon[] = {0xEF, 0x00, 0x58, 0x47}; //错误返回值
uchar xdata outarr[128] = {0xEF}; //返回值
uchar tempchar[4]; //char转float数组
float * tempfloat; //char转float指针
uchar pollflag = 0; //轮询功率开关
//主函数
void main(void)
{
WDTCN = 0xde; //关闭单片机内部看门狗
WDTCN = 0xad; //关闭单片机内部看门狗
OSCILLATOR_Init(); //初始化晶振
ReadData1(); //非中断读取非易失区数据
PORT_Init(); //初始端口
Ext_Interrupt_Init(); //初始化外部中断
UART1_Init(); //初始化串口1
TIMER2_Init(); //初始化定时计数器2,设置功率轮询输出
TIMER3_Init(SYSTEMCLOCK/SAMPLE_RATE); //初始化定时器3,设置数模芯片转换速率
TIMER4_Init (); //初始化定时计数器4,监控串口接受数据间隔
ADC0_Init(); //初始化12位数模转换器
AD0EN = 1; //打开12位数模转换器
EA = 1; //打开所有中断
Manual_LED_GREEN = 0; //关闭Manual灯
Manual_LED_RED = 0; //关闭Manual灯
Auto_LED_GREEN = 0; //关闭Auto灯
Auto_LED_RED = 0; //关闭Auto灯
ACK_LED_GREEN = 0; //关闭ACK灯
ACK_LED_RED = 0; //关闭ACK灯
P4 = 0xFF; //将LED1~4号为灯亮红
while(1)
{
Watchdog = 0; //更新3.3V电源看门狗
Watchdog = 1; //更新3.3V电源看门狗
Auto_LED_RED = Manual_LED_GREEN = 0; //熄灭串口信号灯
if(AckBlink > 0xDFFF) //显示间隔
{
ACK_LED_GREEN = ~ACK_LED_GREEN; //闪烁ACK灯
AckBlink = 0;
}
//delay(1);
AckBlink++; //显示次数累加
}
}
//延迟函数
void delay(uint num)
{
uint i, j;
for(i = 0; i < num; i++)
for(j = 0; j < 200; j++);
}
//晶振初始化
void OSCILLATOR_Init (void)
{
uint i; //定义累加值
OSCXCN = 0x67; //设置外部晶振22.1184MHz
//晶体振荡器方式,晶体振荡器未用或未稳定
for (i=0; i < 256; i++) ; //等待晶振运行
while (!(OSCXCN & 0x80)); //等待晶振运行稳定
OSCICN = 0x88; //选择外部振荡器作为系统时钟
//允许时钟丢失检测器 检测到时钟丢失时间大于 100 微秒时将触发复位
}
//端口初始化
void PORT_Init (void)
{
XBR0 = 0x07; //设置P1.0为TX1,P1.1为RX1
XBR1 = 0x14; //设置P1.2为INT0,P1.3为INT1
XBR2 = 0x44; //设置P0.6为SDA,P0.7为SCL
P0MDOUT = 0xFF; //设置P0为推挽方式
P1MDOUT = 0xFF; //设置P1为推挽方式
P2MDOUT = 0xFF; //设置P2为推挽方式
P3MDOUT = 0xFF; //设置P3为推挽方式
P74OUT = 0xBF; //设置P4-P7为推挽方式
}
//外部中断初始化
void Ext_Interrupt_Init (void)
{
TCON = 0x05; // /INT 0 和INT 1 边沿触发
EX0 = 1; // 使能 /INT0 中断
EX1 = 1; // 使能 /INT1 中断
}
//初始化串口1
void UART1_Init (void)
{
SCON1 = 0x50; //UART1 接收允许
//方式 1 8 位 UART 可变波特率
TMOD &= ~0xF0; //清除定时器 0设置
TMOD |= 0x20; //自动重装载的 8 位计数器/定时器
PCON |= 0x10; //禁止 UART1 的波特率/2 功能
CKCON |= 0x10; //定时器 1 使用系统时钟
TH1 = -((SYSTEMCLOCK/BAUDRATE)/16); //设置定时器1 TH1初值
TL1 = TH1; //设置定时器1 TL1初值
TR1 = 1; //启动定时器1
EIE2 = 0x40; //允许 UART1 中断
EIP2 = 0x40; //外部中断 6 设置为高优先级
}
//初始化定时器3
void TIMER3_Init (int counts)
{
TMR3CN = 0x02; //定时器3禁止;定时器3的时钟源由 T3M TMR3CN.1 位定义;计数器/定时器3使用系统时钟
RCAP3 = -counts; //设置定时器重载初值
TMR3 = RCAP3; //设置定时器初值
EIE2 &= ~0x01; //禁止定时器3中断
TMR3CN |= 0x04; //开始定时器3
}
//初始化12位数模转换器
void ADC0_Init (void)
{
ADC0CN = 0x04; //ADC0禁止,当ADC被允许时除了转换期间之外一直处于跟踪方式
//定时器3溢出启动ADC0转换,数据右对齐
REF0CN = 0x07; //内部电压基准缓冲器工作,内部电压基准提供从 VREF 引脚输出
//内部偏压发生器工作,内部温度传感器工作
AMX0CF = 0x00; //ADC0独立的单端输入
AMX0SL = 0x01; //选择AIN0.1引脚作为ADC0信号输入
ADC0CF = (SYSTEMCLOCK/SAR_CLK) << 3; //ADC0转换时钟= 2.5MHz
ADC0CF |= 0x00; //PGA增益= 1(默认)
EIE2 |= 0x02; //使ADC中断
// EIP2 = 0x03; //ADC 转换结束中断为高优先级
//定时器 3 中断为高优先级
}
//初始化定时器4
void TIMER4_Init ()
{
CKCON &= ~0x40; //定时器 4 使用系统时钟12分频
RCAP4 = -(SYSTEMCLOCK/1000/12*5); //定时器4在1 kHz溢出1ms
TMR4 = RCAP4; //设置定时器初值
T4CON &= ~0x80; //清除初始化标志
EIE2 |= 0x04; //启动定时器4中断
T4CON |= 0x04; //启动定时器4
//T4CON &= ~0x04; //关闭定时器4
}
//初始化定时器2
void TIMER2_Init()
{
CKCON |= 0x20; //定时器 2 使用系统时钟
RCAP2 = OPERATING; //定时器2设置初值
TMR2 = RCAP2; //设置定时器初值
T2CON = 0x04; //使能定时器2自动填装
TF2 = 0; //清除初始化标志
ET2 = 1; //启动定时器2中断
TR2 = 1; //启动定时器2
//PT2 = 1; //定时器2优先级最高
}
//外部中断1
void INT0_ISR (void) interrupt 0
{
pollflag = 1;//打开轮询
}
//外部中断2
void INT1_ISR (void) interrupt 2
{
pollflag = 0;//关闭轮询
}
//功率轮询
void Power_Poll (void) interrupt 5
{
Power = ((float)SampArrAll * Kvaule + Bvalue * (float)SAMP_NUM ) / (float)SAMP_NUM;//计算光功率值dBm
if(Power <= -65) //测试最小值
{
Power = -65;
}
else if(Power >= 10) //测试最大值
{
Power = 10;
}
if(pollflag == 1)
{
float temp = Power; //将功率值读入缓冲区
char * temp1; //零时指针
TMR2 = RCAP2; //设置定时器初值
UART_output_Size = 6;
temp1=(char*)(&temp);
outarr[0] = 0xEF;
outarr[1] = temp1[3]; //将缓冲器数据写入待发区
outarr[2] = temp1[2];
outarr[3] = temp1[1];
outarr[4] = temp1[0];
AcceptReset1(UART_output_Size, &outarr); //中断发送函数
}
TF2 = 0; //清除初始化标志
}
//12位数模转换器中断
void ADC0_ISR (void) interrupt 15
{
if(SampNum == SAMP_NUM) //判断采样次数
{
SampArrAll = SampArr;
SampArr = 0;
SampNum = 0;
}
Samp = ADC0; //写入采样值
SampNum++; //累加
SampArr += ADC0; //读取采样放入数组
AD0INT = 0; //采样中断标志位清零
}
//串口接受间隔
void UART1_time (void) interrupt 16
{
uchar i;
TMR4 = RCAP4; //设置定时器初值
for(i = 0; i < UART_Input_Size; i++)
UART_Input[i] = 0; //清除发送缓冲器
InputCheck = 0; //输入校验和清零
UART_Input_Size = 0; //发送数据计数清零
T4CON &= ~0x80; //清除初始化标志
}
//串口1中断响应函数
void UART1_Interrupt (void) interrupt 20
{
if ((SCON1 & 0x01) == 0x01) //判断RI1接收中断标志
{
TMR4 = RCAP4; //设置定时器初值
T4CON &= ~0x80; //清除初始化标志
if( UART_Input_Size == 0) //检查是否输入了新单词
{
UART_Input_First = 0; //初始化数组序号
}
Byte = SBUF1; //从超级终端读取字符
if (UART_Input_Size < UART_SIZE) //判断读取数据是否超出
{
UART_Input[UART_Input_First] = Byte; //将Byte写入数组
UART_Input_Size++; //更新数组大小
UART_Input_First++; //更新数组位数
Auto_LED_RED = ~Auto_LED_RED; //Auto灯闪烁
}
if(UART_Input_Size > 3)
UART1_Deal(); //串口接受处理函数
SCON1 = (SCON1 & 0xFE); //清零RI1接收中断标志位
}
if ((SCON1 & 0x02) == 0x02) //判断TI1发送中断标志
{
if (UART_output_Size != 0) //如果缓冲区不为空
{
Byte = UART_output[UART_output_First]; //在变量字节中存储一个字符
SBUF1 = Byte; //将一位数据写入缓冲器
UART_output_First++; //数组位累加
UART_output_Size--; //数组总数减一
Manual_LED_GREEN = ~Manual_LED_GREEN; //Manual灯闪烁
}
else
{
UART_output_First = 0; //发送位清零
//UART_output_Size = 0; //发送数量清零
}
SCON1 = (SCON1 & 0xFD); //清零TI1接收中断标志位
}
}
//非易失128Bety Flash写入
void Flash_WRITE(void)
{
uchar i; //初始化累加值
uchar xdata * pwrite; //非易失区指针
uchar * pgen; //常规指针
EA = 0; //关闭所有中断
FLSCL = 0x01; //允许对Flash进行写入
PSCTL = 0x07; //允许对Flash进行擦除
pwrite = 0x1000; //指向Flash的首地址
*pwrite = 0; //擦除该Flash扇区
PSCTL = 0x05; //禁止对Flash进行擦除
pgen = &Permanent_data[0]; //将数组128位 数据写入Flash
for(i = 0; i < 128; i++)
*pwrite++ = *pgen++;
FLSCL = 0x00; //禁止对Flash进行写入
PSCTL = 0x00; //禁止对Flash进行擦除
EA = 1; //打开所有中断
}
//读取非易失Flash数据
void Flash_READ(void)
{
uchar i; //初始化累加值
uchar code *pread; //程序存储空间指针
PSCTL = 0x04; //允许访问128K数据
EA = 0; //关闭所有中断
pread = 0x1000; //指向Flash的首地址
for(i = 0; i < 128; i++) //读取非易失Flash数据start到stop数据
{
Permanent_data[i] = *pread++;
}
PSCTL = 0x00; //禁止对Flash进行擦除
EA = 1; //打开所有中断
}
//串口数据处理
void UART1_Deal(void)
{
uchar i; //累加数
if((UART_Input[0] == 0xEF)) //验证引导码确定发送数据完成
{
InputCheck = 0; //校验和清零
for(i = 0; i < UART_Input_Size - 1; i++) //计算校验和
InputCheck += UART_Input[i];
if(UART_Input[1] == 0x01) //客户写入
{
if((UART_Input[2] == 0x15) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//停止功率轮询
{
pollflag = 0; //关闭定时器2
}
else if((UART_Input[2] == 0x16) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//开始功率轮询
{
pollflag = 1; //开启定时器2
}
}
else if(UART_Input[1] == 0x02) //客户读取
{
if((UART_Input[2] == 0x07) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//读取T1光功率
{
float temp = Power; //将功率值读入缓冲区
char * temp1; //零时指针
UART_output_Size = 6;
temp1=(char*)(&temp);
outarr[0] = 0xEF;
outarr[1] = temp1[3]; //将缓冲器数据写入待发区
outarr[2] = temp1[2];
outarr[3] = temp1[1];
outarr[4] = temp1[0];
AcceptReset(UART_output_Size, &outarr);
}
}
else if(UART_Input[1] == 0x11) //后台写入
{
if((UART_Input[2] == 0x00) && (UART_Input_Size == 0x0A) && (InputCheck == Byte))//写入SN号
{
for(i = 0; i < UART_Input_Size; i++) //将SN号写入到非易失FLASH
{
if((i > 2) && (i < UART_Input_Size -1))
Permanent_data[i-3] = UART_Input[i];
}
Flash_WRITE(); //写入非易失Flash
ReadData(); //读取非易失区数据
AcceptReset(4, &outok);
}
else if((UART_Input[2] == 0x01) && (UART_Input_Size == 0x08) && (InputCheck == Byte))//写入T1K值(1550)
{
for(i = 0; i < UART_Input_Size; i++) //T1K值写入到非易失FLASH
{
if((i > 2) && (i < 7))
Permanent_data[i-3+6] = UART_Input[i];
}
Flash_WRITE(); //写入非易失Flash
ReadData(); //读取非易失区数据
AcceptReset(4, &outok);
}
else if((UART_Input[2] == 0x05) && (UART_Input_Size == 0x08) && (InputCheck == Byte))//写入T1B值(1550)
{
for(i = 0; i < UART_Input_Size; i++) //T1B值写入到非易失FLASH
{
if((i > 2) && (i < 7))
Permanent_data[i-3+6+4*4] = UART_Input[i];
}
Flash_WRITE(); //写入非易失Flash
ReadData(); //读取非易失区数据
AcceptReset(4, &outok);
}
}
else if(UART_Input[1] == 0x12)//后台读取
{
if((UART_Input[2] == 0x00) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//读取SN号
{
UART_output_Size = 8;
Flash_READ(); //重新读取非易失区数据
for(i = 1; i < UART_output_Size-1; i++) //将缓冲器数据写入待发区
{
outarr[i] = Permanent_data[i-1];
}
AcceptReset(UART_output_Size, &outarr);
}
else if((UART_Input[2] == 0x01) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//读取T1K值(1550)
{
UART_output_Size = 6;
Flash_READ(); //重新读取非易失区数据
for(i = 1; i < UART_output_Size-1; i++) //将缓冲器数据写入待发区
{
outarr[i] = Permanent_data[i-1+6];
}
AcceptReset(UART_output_Size, &outarr);
}
else if((UART_Input[2] == 0x05) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//读取T1B值(1550)
{
UART_output_Size = 6;
Flash_READ(); //重新读取非易失区数据
for(i = 1; i < UART_output_Size-1; i++) //将缓冲器数据写入待发区
{
outarr[i] = Permanent_data[i-1+6+4*4];
}
AcceptReset(UART_output_Size, &outarr);
}
else if((UART_Input[2] == 0x19) && (UART_Input_Size == 0x04) && (InputCheck == Byte))//读取T1采样值
{
uint temp = Samp; //将采样值读入缓冲区
UART_output_Size = 4;
outarr[0] = 0xEF;
outarr[2] = temp; //将缓冲器数据写入待发区
temp = temp >> 8;
outarr[1] = temp;
AcceptReset(UART_output_Size, &outarr);
}
}
}
}
//发送接收缓冲器初始化
void AcceptReset(uchar output, uchar outarr[])
{
uchar i;
InputCheck = 0; //输入校验和清零
OutputCheck = 0; //输出校验和清零
UART_output_First = 0; //发送位清零
UART_output_Size = output; //初始化输出数组数量
for(i = 0; i < UART_output_Size - 1; i++)
{
UART_output[i] = outarr[i]; //写入返回数据
OutputCheck += outarr[i]; //计算输出校验和
}
UART_output[UART_output_Size-1] = OutputCheck; //写入返回最后数据
SCON1 = (SCON1 | 0x02); //激活串口RX标记位
}
//中断发送接收缓冲器初始化
void AcceptReset1(uchar output, uchar outarr[])
{
uchar i;
InputCheck = 0; //输入校验和清零
OutputCheck = 0; //输出校验和清零
UART_output_First = 0; //发送位清零
UART_output_Size = output; //初始化输出数组数量
for(i = 0; i < UART_output_Size - 1; i++)
{
UART_output[i] = outarr[i]; //写入返回数据
OutputCheck += outarr[i]; //计算输出校验和
}
UART_output[UART_output_Size-1] = OutputCheck; //写入返回最后数据
SCON1 = (SCON1 | 0x02); //激活串口RX标记位
}
//读取非易失区数据
void ReadData(void)
{
Flash_READ(); //将非易失Flash数据全部读入Permanent_data数组
tempchar[0] = Permanent_data[9]; //读取非易失数据中K值(1550)
tempchar[1] = Permanent_data[8];
tempchar[2] = Permanent_data[7];
tempchar[3] = Permanent_data[6];
tempfloat=(float*)(&tempchar);
Kvaule = *tempfloat;
tempchar[0] = Permanent_data[25]; //读取非易失数据中B值(1550)
tempchar[1] = Permanent_data[24];
tempchar[2] = Permanent_data[23];
tempchar[3] = Permanent_data[22];
tempfloat=(float*)(&tempchar);
Bvalue = *tempfloat;
}
//非中断读取非易失区数据
void ReadData1(void)
{
Flash_READ(); //将非易失Flash数据全部读入Permanent_data数组
tempchar[0] = Permanent_data[9]; //读取非易失数据中K值(1550)
tempchar[1] = Permanent_data[8];
tempchar[2] = Permanent_data[7];
tempchar[3] = Permanent_data[6];
tempfloat=(float*)(&tempchar);
Kvaule = *tempfloat;
tempchar[0] = Permanent_data[25]; //读取非易失数据中B值(1550)
tempchar[1] = Permanent_data[24];
tempchar[2] = Permanent_data[23];
tempchar[3] = Permanent_data[22];
tempfloat=(float*)(&tempchar);
Bvalue = *tempfloat;
}
运行结果及报错内容
前面数据间隔还在20ms,后面就出现300ms延迟。
我的解答思路和尝试过的方法
关闭其他所有中断,尝试只用串口中断连续发送数据,也会周期性出现300ms延迟。
我想要达到的结果
在不使用DMA情况下,实现C8051F020连续发送数据,要求数据间隔保存在20~30ms。