一、AD7739的SPI程序
空闲电平是高电平,所以字节读写程序最后时钟电平是高电平,必须是完整的时钟周期。
void Write7738(unsigned char ch)
{
unsigned char idata, n=8; // 向SDA上发送一位数据字节,共八位 //传输时钟频率117.260KHZ,T=8.528US和波形相符合2020.1.11
CS=0; //但是硬件的SPI计算频率140.625KHZ,实际波形234.625KHZ,T=4.26US不相符合2020.1.11
SCK = 1 ; //时钟置高
while(n--)
{
delay_us(3);
//delay(3);
SCK = 0 ; //时钟置低
if((ch&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
MOSI = 1; // 传送位1
}
else
{
MOSI = 0; // 否则传送位0
}
delay_us(3);
//delay(3);
SCK = 1 ; //时钟置高
ch = ch<<1; // 数据左移一位
}
CS=1; //在103行导致RDY引脚一直是高电平
}
//---------------------------------------------------------------------------------------------
// 函数名称: Read7738
// 入口参数: ch
// 函数功能: 从AD7738寄存器读出一个字节
//---------------------------------------------------------------------------------------------
void Read7738reg()
{
unsigned char i; // 向SDA上发送一位数据字节,共八位
CS=0;
/* unsigned char idata,i, n=8; // 向SDA上发送一位数据字节,共八位
CS=0;
SCK = 1 ; //时钟置高
while(n--) */ //因为这几行没有去掉,所以导致读字节失败,相当于循环了8次读字节,读取最后一次数据,时序中每个字节
//读前和读后都是高电平 ,模拟SPI时序成功2019.9.20
//读出91 后循环读取后面7个字节是FF,读出70,后面7个 字节也是FF。也就是
//DOUT空闲是后并不是保持最后一个位的数据不变,而是变为1。W25Q64空闲是DOUT是高阻态
SCK=1;
MOSI=0;
for(i=0;i<8;i++)
{
delay_us(3);
SCK=0; //单片机写的时候,提供上升沿是为了SPI从设备有锁定时刻;
delay_us(3); //单片机读的时候,单片机引脚自动锁定,提供高低变化是为了从设备提供下降沿,变化输出数据
temp1=temp1<<1;
if(MISO==1)
temp1=temp1|0x01;
else
temp1=temp1&0xFE;
SCK=1;
}
tempbuf=temp1;
CS=1; //有没有此语句,都可以读出来,但是连续时必须有此语句,根据时序要求
//官方例程三个字节连读,也是CS一直是低
}
二、DS1302的SPI程序
空闲电平是低电平,所以字节读写程序最后时钟电平是低电平,必须是完整的时钟周期,并且是片选信号选择后,两个字节连写,写字节的最后下降沿移出读字节的第一位数据到IO引脚。再取消片选信号
void SPI_Write_Byte(uchar tdata)
{
uint i;
// DS1302_CS = 1;
DS1302_CLK = 0;
delayNus(4);
for(i = 0;i < 8;i++)
{
// DS1302_CLK = 0; //此行不修改单独写可以成功但是读字节不能成功,因为空闲状态是低电平,所以必须改到循环体下面才符合写字节时序,2020.2.28
delayNus(3);
if((tdata&0x01) == 0x01)
DS1302_DATA = 1;
else
DS1302_DATA = 0;
DS1302_CLK = 1;
delayNus(3);
DS1302_CLK = 0; //写命令的最后一个下降沿会输出需要读数据的第一位,规格书特别强调注意了2020.3.12
tdata >>=1; //写数据,移位在下面
}
// DS1302_CS = 0; //单字节写完,CS不能拉低,否则写不进去。必须是命令和数据写完再拉低还必须拉低,否则也写不进去.
} //必须和规格书时序一样2020.1.17实际测试
uchar SPI_Read_Byte()
{
uchar i;
uchar temp = 0;
DS1302_DATA = 1;//输入模式 //此语句有无都可以读成功2020.2.29
// DS1302_CS = 1;
// DS1302_CLK = 1; //此A语句导致读字节失败,注释掉和BC配合才读自己成功2020.2.28
delayNus(4);
for(i = 0;i < 8;i++)
{
temp >>= 1; //读数据,移位在上面,如果在最后一行,可能导致多一次移位2020.3.7
// DS1302_CLK = 0; //此B语句导致读字节失败2020.2.28
delayNus(3);
if(DS1302_DATA == 1)
temp |= 0x80;
DS1302_CLK = 1;
delayNus(3);
DS1302_CLK = 0; //此C语句加到此行读字节才成功,也就是B句应改在此行,空闲状态低电平2020.2.28
}
return (temp);
}
小结,这两个芯片都是上升沿采集位数据,下降沿移出下一个位数据到IO引脚。和高低电平的先后没关系。
三、24C02IIC程序
空闲状态是SCK,SDA高电平但是SCK高电平期间采集SDA信号,SDA必须稳定,SCK高电平期间SDA的变化是开始或者停止信号,所以SCL每个时钟周期最后要落到低电平,低电平允许SDA改变。起始和停止信号等信号要先写SDA,再写SCK。
bit AT24C04_Write_Byte(uchar tdata)
{
uchar i;
bit x;
for(i = 0;i < 8;i++)
{
// AT24C04_SCL = 1; //先高电平,可以假设上个周期SDA是0,则,高电平后SDA变为1,
// delayNus(2); //则GAO高电平期间SDA变化,是开始或这停止信号。应该保证高电平期间数据稳定采集2020.3.11
AT24C04_SDA = (bit)(tdata&0x80); //所以应该先让数据稳定再提供高电平信号,采集才能稳定
delayNus(2);
AT24C04_SCL = 1;
delayNus(2);
AT24C04_SCL = 0;
delayNus(4);
tdata <<= 1;
}
AT24C04_SDA = 1; //模拟IIC写字节时候,不等待应答信号,失败。硬件IIC更需要读应答。读的时候不发送非应答信号是可以成功读取的(网上说模拟IIC可以不用)
delayNus(2); //但是硬件IIC必须发送非应答否则读取失败2020.3.11
AT24C04_SCL = 1;
delayNus(2);
x = AT24C04_SDA;
AT24C04_SCL = 0;
return x;
}
uchar AT24C04_Read_Byte()
{
uchar i,temp;
temp = 0;
AT24C04_SDA = 1; //输入模式先写12020.3.11
for(i = 0;i < 8;i++)
{
temp <<= 1;
// AT24C04_SCL = 0;
delayNus(2);
AT24C04_SCL = 1; //高电平读数据,需要注意先提供高电平信号再去读引脚数据。读取的是高电平器件的数据
delayNus(4);
temp |= (unsigned char)AT24C04_SDA; //虽然写反也能读取,但是不推荐2020.3.12
// if(AT24C04_SDA = 1) // 这个方式波形对,但是数码管显示不对2020.3.11
// temp = temp|0x01; // 但是在AD7739-STM32单片机模拟SPI中,这种方式刷程序到电路板上也是可以实现读字节的
// else //ADI官方例程也是这模式,单片机是8位的类似51的单片机
// //temp |= 0; //
// temp = temp& 0xfe;
delayNus(2);
AT24C04_SCL = 0;
// temp <<= 1;
}
return (temp);
}