📄 hjmcu.c
字号:
/**************************************************************************************************
*** 慧净电子1天入门、10天学会、1年精通单片机与C语言视频教程配套程序源码 ***
*** 实验目的:HJ-1G 开发板 AVR配套实验程序 ***
*** MCU: ATmega16 部分C源码可以直接用于项目开发、欢迎复制共享、功德无量、没有版权 ***
*** 作者:慧净免费助学会员
*** 部分源码网上收集整理、如有伤害到你的利益、请来信,hjmcu@163.com 我们的免费助学会员会定期帮你删除掉 ***
*** 编译器:GCC ***
*** 百度交流空间:http://hi.baidu.com/HJMCU WWW.HJMCU.COM ***
*** 论坛交流:http://bbs.hjmcu.com 欢迎来论坛一分钱不要下载配套的仿真电路 ***
*** 配套的硬件学习板网址:http://shop37031453.taobao.com/ ***
*** 日期:2008.8.8 ***
*** 目标:用C语言写程序就这么简单、慧争祝你1天入门、10天学会、1年精通单片机与C语言、找个好工作 ***
***************************************************************************************************/
//慧净电子大学生课程设计专用实验板《HJ-1G》、直接支持AT89S52 STC89C52单片机
//加转接板后支持AVR ATmega16 32 系列单片机、是你学习单片机的得力助手 一板二用,学完51再学AVR
//HJ-1G 学习板加AVR转接板后,装上ATMETAM16 单片机实验
//HJ-1G 学习板AVR单片机实验,FM报警实验
//注意要把JTAGEN 仿真熔丝位设置成1 才能完成本实验,设置成1时不能用仿真口,只能用ISP下载接口下载程序
//本实验要用到电脑串口,可以用串口线接到电脑串口上,也可以用USB转串口线接到电脑USB接口上。
//请用配套的串口调试软件。
//本实验跟据网上的AVR学习笔记有我们的免费助学会员修改成。
#include <avr/io.h> //io端口寄存器配置文件,必须包含
#include <util/delay.h>
//端口声明
/*注:
AVR单片机I/O口模拟I2C总线时建议在外部连接上拉电阻 ,这样可通过改变I/O口输入输出方向的方式
来设置高低电平, 输出口保持不变(0) ,此时如DDRX寄存器为1则变成输出0,若DDRX为0,则I/O口
呈现高阻状态,但因外部的上拉电阻,总线相当于设置高电平,即通过设置DDRX的方式控制总线的高低
*/
#define SCL_INPUT (DDRC &= ~(1 << PC1)) //SCL设置为输入口
#define SCL_OUTPUT (DDRC |= (1 << PC1)) //SCL设置为输出口
#define SCL_LOW (PORTC &= ~(1 << PC1)) //SCL设置为输出低电平
#define SCL_HIGH (PORTC |= (1 << PC1)) //SCL设置为输出高电平
#define SCL_INDATA (PINC & (1 << PC1)) //读取SCL的端口状态
#define SDA_INPUT (DDRC &= ~(1 << PC0)) //SDA设置为输入口
#define SDA_OUTPUT (DDRC |= (1 << PC0)) //SDA设置为输出口
#define SDA_LOW (PORTC &= ~(1 << PC0)) //SDA设置为输出低电平
#define SDA_HIGH (PORTC |= (1 << PC0)) //SDA设置为输出高电平
#define SDA_INDATA (PINC & (1 << PC0)) //读取SDA的端口状态
//变量声明
#define EEPROM_BUS_ADDRESS 0xa0 //器件地址
//函数声明
void Delayus(unsigned int lus); //us延时函数
void Delayms(unsigned int lms); //ms延时函数
void I2C_Init(void); //I2C端口初始化
unsigned char I2C_Start(void); //发送起始信号
void I2C_Stop(void); //发送结束信号
unsigned char I2C_WriteByte(unsigned char dat); //写一个字节
unsigned char I2C_ReadByte(unsigned char ack); //读一个字节
unsigned char EEPROM_ReadByte(unsigned int add); //从固定地址读一字节
void EEPROM_WriteByte(unsigned int add,unsigned char data); //向固定地址写一字节
int main(void) //GCC中main文件必须为返回整形值的函数,没有参数
{
unsigned char i;
PORTB = 0x00;
DDRB = 0xFF; //端口PortB设为输出口,通过相应位LED的变化指示程序运行结果
I2C_Init(); //I2C端口初始化
EEPROM_WriteByte(0x01aa,0x5a); //向固定地址写一字节,向第26页的第十个字节写入数据0x5a
i = EEPROM_ReadByte(0x01aa); //从固定地址读一字节
if(i == 0x5a)
{
PORTB |= 0xFE; //读出的数据正确,则LED0点亮
}
else
{
PORTB |= 0xFD; //读出的数据不正确,则LED1点亮
}
while(1)
{
}
}
//I2C初始化函数
void I2C_Init(void)
{
SCL_LOW; //SCL的PORT状态锁定为0,以后不再改变
SCL_INPUT; //SCL设置为输入口
SDA_LOW; //SDA的PORT状态锁定为0,以后不再改变
SDA_INPUT; //SDA设置为输入口
Delayus(10);
}
//I2C起始条件
unsigned char I2C_Start(void)
{
Delayus(10);
SDA_INPUT; //SDA高电平
Delayus(10); //延时一段时间,使单片机时钟频率符合I2C时钟
SCL_INPUT; //SCL高电平
Delayus(10);
SDA_OUTPUT; //SDA变低,产生由高到低的变化
Delayus(10);
SCL_OUTPUT; //SCL变低,占用总线
Delayus(10);
return 1;
}
//I2C结束条件
void I2C_Stop(void)
{
Delayus(10);
SDA_OUTPUT; //SDA低电平
Delayus(10);
SCL_INPUT; //SCL高电平
Delayus(10);
SDA_INPUT; //SDA变为高电平,产生由低到高的变化
Delayus(10);
}
//向I2C写一个字节
unsigned char I2C_WriteByte(unsigned char dat)
{
unsigned char i,ack; //ack为应答信号
for(i = 0;i < 8; i++) //写8位(1个字节)数据
{
if(dat & 0x80) //写入数据,左移,从最高位写入
{
SDA_INPUT; //如果该位为1,SDA拉高电平
}
else
{
SDA_OUTPUT; // 如果该位为0,SDA拉低电平
}
SCL_INPUT; //SCL高电平,保持数据
Delayus(10);
SCL_OUTPUT; //SCL低电平,数据被送入I2C
dat <<= 1; //需要写入的数据左移一位,送最高位
Delayus(10); //
}
Delayus(10);
SDA_INPUT; //SDA拉高,同时变为输入口
Delayus(10);
SCL_INPUT; //SCL拉高,准备读取应答信号
Delayus(10);
if(SDA_INDATA)
{
ack = 0; //如果此时SDA为高,说明没有应答信号
PORTB |= 0x04; //没有应答信号,点亮LED2
}
else
{
ack = 1; //如果此时SDA为低,说明有应答信号
PORTB |= 0x08; //有应答信号,点亮LED3
}
SCL_OUTPUT; //SCL拉低
Delayus(10);
return ack; //返回应答信号
}
//从I2C读一个字节
unsigned char I2C_ReadByte(unsigned char ack)
{
unsigned char i,dat = 0; //dat为读出的数据
SDA_INPUT; //SDA变为输入口
for(i = 0;i < 8;i++) //读出8位(1个字节)数据
{
Delayus(10);
SCL_OUTPUT; //SCL低,这时允许从I2C发送数据到SDA上
Delayus(10);
SCL_INPUT; //SCL高,准备读取SDA上的数据
Delayus(10);
dat <<= 1; //读取的数据左移一位,从最高位读起
if(SDA_INDATA)
{
dat++; //如果DSA为高,则读取的数据加1
}
Delayus(10);
}
SCL_OUTPUT; //SCL拉低,准备下一个数据变化
Delayus(10);
if(!ack) //
{
SDA_INPUT; //发送NACK
}
else
{
SDA_OUTPUT; //发送ACK
}
Delayus(10);
SCL_INPUT; //SCL高
Delayus(10);
SCL_OUTPUT; //SCL低
Delayus(10);
return (dat); //返回读到的数据
}
//从固定地址读一字节
unsigned char EEPROM_ReadByte(unsigned int add)
{
unsigned char data;
I2C_Start(); //发送起始信号
I2C_WriteByte(EEPROM_BUS_ADDRESS | ((add >> 8) << 1)); //写器件地址和页地址的高3位
I2C_WriteByte(add); //写页地址的低4位和页地址内部偏移量
I2C_Start(); //发送起始信号
I2C_WriteByte(EEPROM_BUS_ADDRESS | 0x01); //发送读命令
data = I2C_ReadByte(0); //读一个字节
I2C_Stop(); //发送结束信号
return data;
}
//向固定地址写一字节
void EEPROM_WriteByte(unsigned int add,unsigned char data)
{
I2C_Start(); //发送起始信号
I2C_WriteByte(EEPROM_BUS_ADDRESS | ((add >> 8) << 1)); //写器件地址和页地址的高3位
I2C_WriteByte(add); //写页地址的低4位和页地址内部偏移量
I2C_WriteByte(data); //写一个字节数据
I2C_Stop(); //发送结束信号
Delayms(10);
}
//us级别的延时函数
void Delayus(unsigned int lus)
{
while(lus--)
{
_delay_loop_2(3); //_delay_loop_2(1)是延时4个时钟周期,参数为3则延时12
//个时钟周期,本实验用12M晶体,则12个时钟周期为12/12=1us
}
}
//ms级别的延时函数
void Delayms(unsigned int lms)
{
while(lms--)
{
Delayus(1000); //延时1ms
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -