来源:http://bbs.21ic.com/icview-688762-1-1.html
这次调试
stm8
的硬件真是艰辛,没有想到这样恼火!整整搞啦 我
5
天的时间,参考网上很多例子和风驰的
stm8
讲解!和自己的实践,终于搞通啦!特将自己在调试过程中一些心得希望的大家分享。由于本人水平有限也希望大家指出不足和错误的地方!
Stm8
不需要专门配置
GPIO
口,执行初始化就可以啦!有些
stm8
需要打开
EEPROM
设置
I2C(
看官方文档
)
。
Stm8
主要靠
SR1
和
SR3
状态寄存器判断
I2C
的情况
(while(!XXXXX)
就是出自这里
)
,多半大家调不通!就是卡在这里(需要注意的是 寄存器有些位,只要读寄存器就可以清除,在仿真的时候,最好不要打开寄存器页面)。这里分软故障和硬故障:
首先是硬故障
:
一般是
stm8
芯片
IO
口坏啦,有些时候
stm8
能够写程序而且
IO
别的功能都是好的,单单是
I2C
用不起!还有就是
IO
上拉电压不够!我就遇到这样的问题,我
IO
加上逻辑分析仪后就可以调通,不加就通不了。这个也搞啦我很久。
软故障
:
一般主要是设置
CR1
和
CR2
问题,只要按照我的参考程序设置就可以!
我详细的讲讲,寄存器
I2c_CR2
应答使能位(位
2
)
ack
。首先是理解:官方文档上面说的是
ack
应答使能,对是使能!很多人包括我自己 开始都认为是发送
ack,
导致每次
stm8
收到数据后,我们都手动在每次收到字节后加
I2C_AcknowledgeConfig(I2C_ACK_CURR)
无任何意义,因为在接收模式下,收到完整字节后
,
自动发送
ack(
提前是
CR2 ack
位使能,不需要专门
CR2 ack
位置
1) ,都是软件虚拟
I2C
用多啦!想当然啦!
还有就是使用这个
ack
!设置
ACK
都必须在接收字节前,也就是说为个在收到最后一个字节后产生一个
NACK
脉冲,在读倒数第二个数据字节之后
,
必须清除
ack
位
(ack=0)
!设置
ack
同理! 还有需要 主要的地方 如果设置
ack=0;
下次需要重新产生
ack
的时候!需要手动置位
ack!
记住在开始接收之前!如果你只有一个字节正确,后面全部是
0xFF...
可能就是这个问题(切记!切记!很多例子都没有加上这句,包括风驰 的例子!! 不过 他没有加循环
!
如果他再循环一次就会出现问题。)
随便说说仿真调试!在调试过程中,最好不要打开
I2C
寄存器看!因为对寄存器的读,也会造成寄存器有些位重置!直接按
Go,
然后暂停。进去程序看卡在那里啦。
操作库和寄存器编写程序,其实没有分别!不过为啦更好的理解,我在这里是操作寄存器!网上有人说加入中断会对
I2C
产生影响,我这里没有加中断。希望有后来人补全!反正我这几天运行没有发现问题!
附录
1
主要
I2C
程序
/*******************************************************************************
* 名称: Read_8816
* 功能: 读取温度数据
* 形参: *pBuffer 返回读取数据指针
index 温度寄存器地址
NumByteToRead 需要读取字节数
/*******************************************************************************
* 名称: Read_8816
* 功能: 读取温度数据
* 形参: *pBuffer 返回读取数据指针
index 温度寄存器地址
NumByteToRead 需要读取字节数
* 返回: 无
* 说明: 该函数直接操作stm8寄存器!
寄存器I2c_CR2 应答使能位(位2)ack=1,当stm8接受到数据后,自动发送ACK.不需要手动ack
为个在收到最后一个字节后产生一个NACK 脉冲,在读倒数第二个数据字节之后,必须清除ack位(ack=0)
当接收到最后一个字节,需要重新使能ack=1.(如果不设置ack=1,循环开始后,收到第一个字节stm8不会发送ack,造成除每次循环第一个字节正常外,后面收到
的数据都是0xFF....stm8一直不给ack)
需要注意的是 寄存器有些位,只要读寄存器就可以清除(不需要专门写入寄存器,在仿真的时候,最好不要打开寄存器页面),
******************************************************************************/
void Read_8816(u8 *pBuffer, u8 index, u8 NumByteToRead)
{
while(I2C->SR3 & 0x02); //等待总线空闲 检测i2c-SR3 busy位
//以下见stm8s中文数据手册P251(图96主设备发送模式发送序列图)
//S 起始条件
I2C->CR2 |= 0x01; //产生起始位 CR2 start位
//EV5:SB=1,读SR1 然后将地址写入DR寄存器将清除该标志。
while(!(I2C->SR1 & 0x01)); //等待START发送完 E5
//ADDRESS (发送模式)
I2C->DR = 0x00; //发送MLX90615器件地址(最后一位是0,表示发送)
while(!(I2C->SR1 & 0x02)); //等特7位器件地址发送完并且收到ack,ADDR置1
//EV6:ADDR 在软件读取SR1后,对SR3寄存器读操作 将清除改位
I2C->SR1; //见P251 读SR1 (实验证明可以不要)
I2C->SR3; //然后读SR3 清 ADDR(等于库函数I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED))
//DATA 发送寄存器地址
I2C->DR = (u8)(index);
//EV8_2 TxE=1 ,BTF=1,产生停止条件时由硬件清除。
while(!(I2C->SR1 & 0x84)); //检测SR1 TXE1 BTF位置(只有当stm8收到ack,TxE才会置1,其实这句相当于判断收到ack没有?)
//在发送地址和清除ADDR 之后,I2C接口进入主设备接收模式。以下见stm8s中文数据手册P252(图97主设备接收模式接收序列图)
//S 重复起始条件
I2C->CR2 |= 0x01; //产生重复起始位
//EV5:SB=1,读SR1 然后将地址写入DR寄存器将清除该标志。
while(!(I2C->SR1 & 0x01)); //等待START发送完
//ADDRESS (接收)
I2C->DR = 0x01; //发送MLX90615器件地址(最后一位是1,表示接收),发送完后自动发送ack(提前是CR2 ack位使能)
//EV6:ADDR 在软件读取SR1后,对SR3寄存器读操作 将清除改位
while(!(I2C->SR1 & 0x02)); //等特7位器件地址发送完并且收到ack,ADDR置1
I2C->SR1; //见P251 读SR1 (实验证明可以不要)
I2C->SR3; //然后读SR3 清 ADDR(等于库函数I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED))
//循环读取数据
while(NumByteToRead)
{
//EV7_1 :RxNE=1 ,读DR寄存器清除该标志。设置ACK=0和STOP 请求。(在接收最后一个字节前)
if(NumByteToRead == 1) //实验证明在最后一个字节前后都一样
{
I2C->CR2 &= ~0x04; //ack使能
I2C->CR2 |= 0x02; //停止位产生stop
}
///测试EV7 RxNE=1(收到一个字节后RxNE置1) ,判断DR寄存器有数据
if(I2C->SR1 & 0x40)
{
*pBuffer=I2C->DR;//在接收模式下,收到完整字节后,自动发送ack(提前是CR2 ack位使能,不需要专门CR2 ack位置1)
//在风驰里面例子,在每次收到字节后加I2C_AcknowledgeConfig(I2C_ACK_CURR)无任何意义,
pBuffer++;
NumByteToRead--;
}
}
I2C->CR2 |= 0x04;//为一下循环开始 设置 ack使能,上面 EV7_1设置ack=0发送stop后;需要手动设置ack=1使能,必要在接收数据之前
//切记!切记!很多例子都没有加上这句,包括风驰 的例子!! 不过 他没有加循环! 如果他在循环一次就会出现问题。CR2 ack位其实就是使能的意思!!很多人都理解成需要手动设置!
// 都是软件模拟I2C 搞太多!! 想当然啦!没有仔细看官方的文档!
}