A/D和D/A的学习
17.1 A/D和D/A的基本概念
A/D是模拟量到数字量的转换,依靠的是模数转换器(Analog to Digital Converter),简称ADC;D/A是数字量到模拟量的转换,依靠的是数模转换器(Digital to Analog Converter),简称DAC。他们的道理是完全一样的,只是转换方向不同,因此我们讲解过程主要以A/D为例来讲解。
17.2 A/D的主要指标
17.3 PCF8591的硬件接口
17.4 PCF8591的软件编程
/***********************lcd1602.c文件程序源代码*************************/
#include <reg52.h> #define LCD1602_DB P0 sbit LCD1602_RS = P1^;
sbit LCD1602_RW = P1^;
sbit LCD1602_E = P1^; void LcdWaitReady() //等待液晶准备好
{
unsigned char sta; LCD1602_DB = 0xFF;
LCD1602_RS = ;
LCD1602_RW = ;
do
{
LCD1602_E = ;
sta = LCD1602_DB; //读取状态字
LCD1602_E = ;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
void LcdWriteCmd(unsigned char cmd) //写入命令函数
{
LcdWaitReady();
LCD1602_RS = ;
LCD1602_RW = ;
LCD1602_DB = cmd;
LCD1602_E = ;
LCD1602_E = ;
}
void LcdWriteDat(unsigned char dat) //写入数据函数
{
LcdWaitReady();
LCD1602_RS = ;
LCD1602_RW = ;
LCD1602_DB = dat;
LCD1602_E = ;
LCD1602_E = ;
}
void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str) //显示字符串,屏幕起始坐标(x,y),字符串指针str
{
unsigned char addr; //由输入的显示坐标计算显示RAM的地址
if (y == )
addr = 0x00 + x; //第一行字符地址从0x00起始
else
addr = 0x40 + x; //第二行字符地址从0x40起始 //由起始显示RAM地址连续写入字符串
LcdWriteCmd(addr | 0x80); //写入起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str);
str++;
}
}
void LcdInit() //液晶初始化函数
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
/***********************I2C.c文件程序源代码*************************/
#include <reg52.h>
#include <intrins.h> #define I2CDelay() {_nop_();_nop_();_nop_();_nop_();} sbit I2C_SCL = P3^;
sbit I2C_SDA = P3^; void I2CStart() //产生总线起始信号
{
I2C_SDA = ; //首先确保SDA、SCL都是高电平
I2C_SCL = ;
I2CDelay();
I2C_SDA = ; //先拉低SDA
I2CDelay();
I2C_SCL = ; //再拉低SCL
}
void I2CStop() //产生总线停止信号
{
I2C_SCL = ; //首先确保SDA、SCL都是低电平
I2C_SDA = ;
I2CDelay();
I2C_SCL = ; //先拉高SCL
I2CDelay();
I2C_SDA = ; //再拉高SDA
I2CDelay();
}
bit I2CWrite(unsigned char dat) //I2C总线写操作,待写入字节dat,返回值为应答状态
{
bit ack; //用于暂存应答位的值
unsigned char mask; //用于探测字节内某一位值的掩码变量 for (mask=0x80; mask!=; mask>>=) //从高位到低位依次进行
{
if ((mask&dat) == ) //该位的值输出到SDA上
I2C_SDA = ;
else
I2C_SDA = ;
I2CDelay();
I2C_SCL = ; //拉高SCL
I2CDelay();
I2C_SCL = ; //再拉低SCL,完成一个位周期
}
I2C_SDA = ; //8位数据发送完后,主机释放SDA,以检测从机应答
I2CDelay();
I2C_SCL = ; //拉高SCL
ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值
I2CDelay();
I2C_SCL = ; //再拉低SCL完成应答位,并保持住总线 return (~ack); //应答值取反以符合通常的逻辑:0=不存在或忙或写入失败,1=存在且空闲或写入成功
}
unsigned char I2CReadNAK() //I2C总线读操作,并发送非应答信号,返回值为读到的字节
{
unsigned char mask;
unsigned char dat; I2C_SDA = ; //首先确保主机释放SDA
for (mask=0x80; mask!=; mask>>=) //从高位到低位依次进行
{
I2CDelay();
I2C_SCL = ; //拉高SCL
if(I2C_SDA == ) //读取SDA的值
dat &= ~mask; //为0时,dat中对应位清零
else
dat |= mask; //为1时,dat中对应位置1
I2CDelay();
I2C_SCL = ; //再拉低SCL,以使从机发送出下一位
}
I2C_SDA = ; //8位数据发送完后,拉高SDA,发送非应答信号
I2CDelay();
I2C_SCL = ; //拉高SCL
I2CDelay();
I2C_SCL = ; //再拉低SCL完成非应答位,并保持住总线 return dat;
}
unsigned char I2CReadACK() //I2C总线读操作,并发送应答信号,返回值为读到的字节
{
unsigned char mask;
unsigned char dat; I2C_SDA = ; //首先确保主机释放SDA
for (mask=0x80; mask!=; mask>>=) //从高位到低位依次进行
{
I2CDelay();
I2C_SCL = ; //拉高SCL
if(I2C_SDA == ) //读取SDA的值
dat &= ~mask; //为0时,dat中对应位清零
else
dat |= mask; //为1时,dat中对应位置1
I2CDelay();
I2C_SCL = ; //再拉低SCL,以使从机发送出下一位
}
I2C_SDA = ; //8位数据发送完后,拉低SDA,发送应答信号
I2CDelay();
I2C_SCL = ; //拉高SCL
I2CDelay();
I2C_SCL = ; //再拉低SCL完成应答位,并保持住总线 return dat;
}
/***********************main.c文件程序源代码*************************/
#include <reg52.h> bit flag300ms = ; //300ms定时标志
unsigned char T0RH = ; //T0重载值的高字节
unsigned char T0RL = ; //T0重载值的低字节 unsigned char GetADCValue(unsigned char chn);
void ValueToString(unsigned char *str, unsigned char val);
void ConfigTimer0(unsigned int ms);
extern void LcdInit();
extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat); void main ()
{
unsigned char val;
unsigned char str[]; EA = ; //开总中断
ConfigTimer0(); //配置T0定时10ms
LcdInit(); //初始化液晶
LcdShowStr(, , "AIN0 AIN1 AIN3"); //显示通道指示 while()
{
if (flag300ms)
{
flag300ms = ;
//显示通道0的电压
val = GetADCValue(); //获取ADC通道0的转换值
ValueToString(str, val); //转为字符串格式的电压值
LcdShowStr(, , str); //显示到液晶上
//显示通道1的电压
val = GetADCValue();
ValueToString(str, val);
LcdShowStr(, , str);
//显示通道3的电压
val = GetADCValue();
ValueToString(str, val);
LcdShowStr(, , str);
}
}
} unsigned char GetADCValue(unsigned char chn) //读取当前的ADC转换值,chn为ADC通道号0-3
{
unsigned char val; I2CStart();
if (!I2CWrite(0x48<<)) //寻址PCF8591,如未应答,则停止操作并返回0
{
I2CStop();
return ;
}
I2CWrite(0x40|chn); //写入控制字节,选择转换通道
I2CStart();
I2CWrite((0x48<<)|0x01); //寻址PCF8591,指定后续为读操作
I2CReadACK(); //先空读一个字节,提供采样转换时间
val = I2CReadNAK(); //读取刚刚转换完的值
I2CStop(); return val;
}
void ValueToString(unsigned char *str, unsigned char val) //ADC转换值转为实际电压值的字符串形式
{
val = (val*) / ; //电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数
str[] = (val/) + ''; //整数位字符
str[] = '.'; //小数点
str[] = (val%) + ''; //小数位字符
str[] = 'V'; //电压单位
str[] = '\0'; //结束符
} void ConfigTimer0(unsigned int ms) //T0配置函数
{
unsigned long tmp; tmp = / ; //定时器计数频率
tmp = (tmp * ms) / ; //计算所需的计数值
tmp = - tmp; //计算定时器重载值
tmp = tmp + ; //修正中断响应延时造成的误差 T0RH = (unsigned char)(tmp >> ); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = ; //使能T0中断
TR0 = ; //启动T0
}
void InterruptTimer0() interrupt //T0中断服务函数
{
static unsigned char tmr300ms = ; TH0 = T0RH; //定时器重新加载重载值
TL0 = T0RL;
tmr300ms++;
if (tmr300ms >= ) //定时300ms
{
tmr300ms = ;
flag300ms = ;
}
}
17.5 A/D差分输入信号
17.6 D/A输出
/***********************I2C.c文件程序源代码*************************/
略
/***********************keyboard.c文件程序源代码*************************/
#include <reg52.h> sbit KEY_IN_1 = P2^; //矩阵按键的扫描输入引脚1
sbit KEY_IN_2 = P2^; //矩阵按键的扫描输入引脚2
sbit KEY_IN_3 = P2^; //矩阵按键的扫描输入引脚3
sbit KEY_IN_4 = P2^; //矩阵按键的扫描输入引脚4
sbit KEY_OUT_1 = P2^; //矩阵按键的扫描输出引脚1
sbit KEY_OUT_2 = P2^; //矩阵按键的扫描输出引脚2
sbit KEY_OUT_3 = P2^; //矩阵按键的扫描输出引脚3
sbit KEY_OUT_4 = P2^; //矩阵按键的扫描输出引脚4 const unsigned char code KeyCodeMap[][] = { //矩阵按键编号到PC标准键盘键码的映射表
{ '', '', '', 0x26 }, //数字键1、数字键2、数字键3、向上键
{ '', '', '', 0x25 }, //数字键4、数字键5、数字键6、向左键
{ '', '', '', 0x28 }, //数字键7、数字键8、数字键9、向下键
{ '', 0x1B, 0x0D, 0x27 } //数字键0、ESC键、 回车键、 向右键
};
unsigned char pdata KeySta[][] = { //全部矩阵按键的当前状态
{, , , },
{, , , },
{, , , },
{, , , }
}; extern void KeyAction(unsigned char keycode); void KeyDriver() //按键动作驱动函数
{
unsigned char i, j;
static unsigned char pdata backup[][] = { //按键值备份,保存前一次的值
{, , , },
{, , , },
{, , , },
{, , , }
}; for (i=; i<; i++) //循环扫描4*4的矩阵按键
{
for (j=; j<; j++)
{
if (backup[ i][j] != KeySta[ i][j]) //检测按键动作
{
if (backup[ i][j] != ) //按键按下时执行动作
{
KeyAction(KeyCodeMap[ i][j]); //调用按键动作函数
}
backup[ i][j] = KeySta[ i][j];
}
}
}
}
void KeyScan() //按键扫描函数
{
unsigned char i;
static unsigned char keyout = ; //矩阵按键扫描输出计数器
static unsigned char keybuf[][] = { //按键扫描缓冲区,保存一段时间内的扫描值
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF},
{0xFF, 0xFF, 0xFF, 0xFF}
}; //将一行的4个按键值移入缓冲区
keybuf[keyout][] = (keybuf[keyout][] << ) | KEY_IN_1;
keybuf[keyout][] = (keybuf[keyout][] << ) | KEY_IN_2;
keybuf[keyout][] = (keybuf[keyout][] << ) | KEY_IN_3;
keybuf[keyout][] = (keybuf[keyout][] << ) | KEY_IN_4; //消抖后更新按键状态
for (i=; i<; i++) //每行4个按键,所以循环4次
{
if ((keybuf[keyout][ i] & 0x0F) == 0x00)
{ //连续4次扫描值为0,即16ms(4*4ms)内都只检测到按下状态时,可认为按键已按下
KeySta[keyout][ i] = ;
}
else if ((keybuf[keyout][ i] & 0x0F) == 0x0F)
{ //连续4次扫描值为1,即16ms(4*4ms)内都只检测到弹起状态时,可认为按键已弹起
KeySta[keyout][ i] = ;
}
} //执行下一次的扫描输出
keyout++;
keyout &= 0x03;
switch (keyout)
{
case : KEY_OUT_4 = ; KEY_OUT_1 = ; break;
case : KEY_OUT_1 = ; KEY_OUT_2 = ; break;
case : KEY_OUT_2 = ; KEY_OUT_3 = ; break;
case : KEY_OUT_3 = ; KEY_OUT_4 = ; break;
default: break;
}
}
/***********************main.c文件程序源代码*************************/
#include <reg52.h> unsigned char T0RH = ; //T0重载值的高字节
unsigned char T0RL = ; //T0重载值的低字节 void ConfigTimer0(unsigned int ms);
extern void KeyScan();
extern void KeyDriver();
extern void I2CStart();
extern void I2CStop();
extern bit I2CWrite(unsigned char dat); void main ()
{
EA = ; //开总中断
ConfigTimer0(); //配置T0定时1ms while()
{
KeyDriver();
}
} void SetDACOut(unsigned char val) //设置DAC输出值
{
I2CStart();
if (!I2CWrite(0x48<<)) //寻址PCF8591,如未应答,则停止操作并返回
{
I2CStop();
return;
}
I2CWrite(0x40); //写入控制字节
I2CWrite(val); //写如DA值
I2CStop();
}
void KeyAction(unsigned char keycode) //按键动作函数,根据键码执行相应动作
{
static unsigned char volt = ; //输出电压值,隐含了一位十进制小数位 if (keycode == 0x26) //向上键,增加0.1V电压值
{
if (volt < )
{
volt++;
SetDACOut(volt*/); //转换为AD输出值
}
}
else if (keycode == 0x28) //向下键,减小0.1V电压值
{
if (volt > )
{
volt--;
SetDACOut(volt*/); //转换为AD输出值
}
}
}
void ConfigTimer0(unsigned int ms) //T0配置函数
{
unsigned long tmp; tmp = / ; //定时器计数频率
tmp = (tmp * ms) / ; //计算所需的计数值
tmp = - tmp; //计算定时器重载值
tmp = tmp + ; //修正中断响应延时造成的误差 T0RH = (unsigned char)(tmp >> ); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = ; //使能T0中断
TR0 = ; //启动T0
}
void InterruptTimer0() interrupt //T0中断服务函数
{
TH0 = T0RH; //定时器重新加载重载值
TL0 = T0RL;
KeyScan();
}
17.7 PCF8591信号发生器
/***********************I2C.c文件程序源代码*************************/
略
/***********************keyboard.c文件程序源代码********************/
略
/***********************main.c文件程序源代码************************/
#include <reg52.h> unsigned char T0RH = ; //T0重载值的高字节
unsigned char T0RL = ; //T0重载值的低字节
unsigned char T1RH = ; //T1重载值的高字节
unsigned char T1RL = ; //T1重载值的低字节 unsigned char code SinWave[] = { //正弦波波表
, , , , , , , , , , , , , ,
, ,, , , , , , , , , , , ,
, , , ,
};
unsigned char code TriWave[] = { //三角波波表
, , , , , , , , , , , , , ,
, ,, , , , , , , , , , , ,
, , , ,
};
unsigned char code SawWave[] = { //锯齿波表
, , , , , , , , , , , , , , ,
,, , , , , , , , , , , , ,
, , ,
};
unsigned char code *pWave; //波表指针 void SetWaveFreq(unsigned char freq);
void ConfigTimer0(unsigned int ms);
extern void KeyScan();
extern void KeyDriver();
extern void I2CStart();
extern void I2CStop();
extern bit I2CWrite(unsigned char dat); void main ()
{
EA = ; //开总中断
ConfigTimer0(); //配置T0定时1ms
pWave = SinWave; //默认正弦波
SetWaveFreq(); //默认频率10Hz while()
{
KeyDriver();
}
} void KeyAction(unsigned char keycode) //按键动作函数,根据键码执行相应动作
{
static unsigned char wave = ; if (keycode == 0x26) //向上键,切换波形
{
if (wave == )
{
wave = ;
pWave = TriWave;
}
else if (wave == )
{
wave = ;
pWave = SawWave;
}
else
{
wave = ;
pWave = SinWave;
}
}
}
void SetDACOut(unsigned char val) //设置DAC输出值
{
I2CStart();
if (!I2CWrite(0x48<<)) //寻址PCF8591,如未应答,则停止操作并返回
{
I2CStop();
return;
}
I2CWrite(0x40); //写入控制字节
I2CWrite(val); //写如DA值
I2CStop();
}
void SetWaveFreq(unsigned char freq) //设置输出波形的频率
{
unsigned long tmp; tmp = (/) / (freq*); //定时器计数频率,是波形频率的32倍
tmp = - tmp; //计算定时器重载值
tmp = tmp + ; //修正中断响应延时造成的误差 T1RH = (unsigned char)(tmp >> ); //定时器重载值拆分为高低字节
T1RL = (unsigned char)tmp;
TMOD &= 0x0F; //清零T1的控制位
TMOD |= 0x10; //配置T1为模式1
TH1 = T1RH; //加载T1重载值
TL1 = T1RL;
ET1 = ; //使能T1中断
PT1 = ; //设置为高优先级
TR1 = ; //启动T1
}
void ConfigTimer0(unsigned int ms) //T0配置函数
{
unsigned long tmp; tmp = / ; //定时器计数频率
tmp = (tmp * ms) / ; //计算所需的计数值
tmp = - tmp; //计算定时器重载值
tmp = tmp + ; //修正中断响应延时造成的误差 T0RH = (unsigned char)(tmp >> ); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0的控制位
TMOD |= 0x01; //配置T0为模式1
TH0 = T0RH; //加载T0重载值
TL0 = T0RL;
ET0 = ; //使能T0中断
TR0 = ; //启动T0
}
void InterruptTimer0() interrupt //T0中断服务函数
{
TH0 = T0RH; //定时器重新加载重载值
TL0 = T0RL;
KeyScan();
}
void InterruptTimer1() interrupt //T1中断服务函数
{
static unsigned char i = ; TH1 = T1RH; //定时器重新加载重载值
TL1 = T1RL;
//循环输出波表中的数据
SetDACOut(pWave[ i]);
i++;
if (i >= )
{
i = ;
}
}
17.8 作业
[size=12.0000pt]3、将信号发生器的程序改装,可以通过按键实现频率的调整。
A/D和D/A的学习的更多相关文章
- 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代
2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...
- Angular2学习笔记(1)
Angular2学习笔记(1) 1. 写在前面 之前基于Electron写过一个Markdown编辑器.就其功能而言,主要功能已经实现,一些小的不影响使用的功能由于时间关系还没有完成:但就代码而言,之 ...
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- 消息队列——RabbitMQ学习笔记
消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- Unity3d学习 制作地形
这周学习了如何在unity中制作地形,就是在一个Terrain的对象上盖几座小山,在山底种几棵树,那就讲一下如何完成上述内容. 1.在新键得项目的游戏的Hierarchy目录中新键一个Terrain对 ...
- 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...
- 菜鸟Python学习笔记第一天:关于一些函数库的使用
2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
随机推荐
- css引入的两种方法link和@import的区别和用法
link和@import都是HTML中引入CSS的语法单词. 两者的基本语法 link语法结构 <link href="外部CSS文件的URL路径" rel="st ...
- APP-SERVICE-SDK:setStorageSync:fail;at page/near/pages/shops/shops page lifeCycleMethod onUnload function
APP-SERVICE-SDK:setStorageSync:fail;at page/near/pages/shops/shops page lifeCycleMethod onUnload fun ...
- Echarts使用Ajax异步获得数据的前端json格式转化问题
利用Ajax获取后台传来的data,官网都有example 但如果后台传来的数据是String格式的,则应该在Ajax的done方法中第一句加上格式转换的语句 data = JSON.parse(da ...
- SQL Server 数据库try catch 存储过程
SQL Server 在生产环境中这样写存储过程的坑都避免了吗? 原文链接: http://www.cnblogs.com/chenmh/p/7856777.html 概述 最近因为业务的需求写了一段 ...
- Visual Studio常用插件整理
Visual Studio Tools for Git GIT代码管理工具 Resharper 代码生成工具 CSOutline2017 语法级别的代码折叠 ...
- DRF 版本 认证
DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢大家都知道我们开发项目是有多个版本的 当我们项目越来越更新~版本就越来越多我们不可能新的版本出了~以前旧的版本 ...
- 自己实现memcpy,strcpy与strncpy
内存拷贝函数 //实现拷贝不重叠的内存块 void* memcpy1(void* dst,const void* src,size_t count) { char* pTo = (char*)dst; ...
- 云服务器ECS
云服务器Elastic Compute Service(ECS)是阿里云提供的一种基础云计算服务.使用云服务器ECS就像使用水.电.煤气等资源一样便捷.高效.您无需提前采购硬件设备,而是根据业务需要, ...
- Android EditView 获取焦点 不弹出软键盘
很简单的做法: 找到AndroidManifest.xml文件 然后在对应的activity中增加android:windowSoftInputMode="adjustPan" & ...
- Power Network POJ - 1459 网络流 DInic 模板
#include<cstring> #include<cstdio> #define FOR(i,f_start,f_end) for(int i=f_startl;i< ...