SAM4E-EK开发板代码解读11——EEPROM
本帖最后由 Mcuzone_TKN 于 2020-5-14 14:29 编辑关键词:Microchip AtmelSAM4E SAM4E-EKSAM4E16E 芯片 PCF8563 EEPROM
概述:简要解读EEPROM
EEPROM(带电可擦除编程只读存储器)是用户可更改的只读存储器(ROM),其可通过高于普通电压的作用来擦除和重新编程(重写)。
EEPROM以Byte为最小修改单位,不必将资料全部洗掉才能写入。
打开产品光盘SAM4E16E-EK/SAM4E16E-EK中文资料/softpack软件包/Atmel Studio 7,打开13a_TWI_MASTER_EXAMPLE_eeprom例子。
int main(void)
{
uint32_t i;
twi_options_t opt;
twi_packet_t packet_tx, packet_rx;
//初始化SAM系统
sysclk_init();
board_init();
//初始化控制台uart
configure_console();
//输出示例信息
puts(STRING_HEADER);
//配置systick为1ms
puts("Configure system tick to get 1ms tick period.\r");
if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
puts("-F- Systick configuration error\r");
while (1);
}
//使能TWI外部时钟
pmc_enable_periph_clk(BOARD_ID_TWI_EEPROM);
//配置TWI驱动程序的选项
opt.master_clk = sysclk_get_cpu_hz();
opt.speed = TWI_CLK;
//配置要传送的数据包
packet_tx.chip = AT24C_ADDRESS;
packet_tx.addr = EEPROM_MEM_ADDR >> 8;
packet_tx.addr = EEPROM_MEM_ADDR;
packet_tx.addr_length = EEPROM_MEM_ADDR_LENGTH;
packet_tx.buffer = (uint8_t *) test_data_tx;
packet_tx.length = TEST_DATA_LENGTH;
//配置要接收的数据包
packet_rx.chip = packet_tx.chip;
packet_rx.addr = packet_tx.addr;
packet_rx.addr = packet_tx.addr;
packet_rx.addr_length = packet_tx.addr_length;
packet_rx.buffer = gs_uc_test_data_rx;
packet_rx.length = packet_tx.length;
if (twi_master_init(BOARD_BASE_TWI_EEPROM, &opt) != TWI_SUCCESS) {
puts("-E-\tTWI master initialization failed.\r");
while (1) ;
}
//发送测试模式到EEPROM
if (twi_master_write(BOARD_BASE_TWI_EEPROM, &packet_tx) != TWI_SUCCESS) {
puts("-E-\tTWI master write packet failed.\r");
while (1) ;
}
printf("Write:\tOK!\n\r");
//等待至少10ms
mdelay(WAIT_TIME);
//从EEPROM获得内存
if (twi_master_read(BOARD_BASE_TWI_EEPROM, &packet_rx) != TWI_SUCCESS) {
puts("-E-\tTWI master read packet failed.\r");
while (1) ;
}
puts("Read:\tOK!\r");
//比较发送和接收
for (i = 0; i < TEST_DATA_LENGTH; i++) {
if (test_data_tx != gs_uc_test_data_rx) {
puts("Data comparison:\tUnmatched!\r");
while (1) ;
}
}
puts("Data comparison:\tMatched!\r");
while (1) ;
}
初始化
#define I2C_FAST_MODE_SPEED400000
#define TWI_CLK_DIVIDER 2
#define TWI_CLK_CALC_ARGU 4
#define TWI_CLK_DIV_MAX 0xFF
#define TWI_CLK_DIV_MIN 7
//使能TWI主模式
void twi_enable_master_mode(Twi *p_twi)
{
//设置主禁用位和从禁用位
p_twi->TWI_CR = TWI_CR_MSDIS;
p_twi->TWI_CR = TWI_CR_SVDIS;
//设置主使能位
p_twi->TWI_CR = TWI_CR_MSEN;
}
//暂时禁用TWI主控模式
void twi_disable_master_mode(Twi *p_twi)
{
//设置主禁用位
p_twi->TWI_CR = TWI_CR_MSDIS;
}
//初始化TWI模式
如果初始化成功 就返回TWI_SUCCESS,否则返回错误代码
uint32_t twi_master_init(Twi *p_twi, const twi_options_t *p_opt)
{
uint32_t status = TWI_SUCCESS;
//关闭TWI中断
p_twi->TWI_IDR = ~0UL;
//虚拟读状态寄存器
p_twi->TWI_SR;
//复位TWI外设
twi_reset(p_twi);
twi_enable_master_mode(p_twi);
//选择速度
if (twi_set_speed(p_twi, p_opt->speed, p_opt->master_clk) == FAIL) {
//所需的速度设置被拒绝
status = TWI_INVALID_ARGUMENT;
}
if (p_opt->smbus == 1) {
p_twi->TWI_CR = TWI_CR_QUICK;
}
return status;
}
//设置I2C总线速度与时钟频率
uint32_t twi_set_speed(Twi *p_twi, uint32_t ul_speed, uint32_t ul_mck)
{
uint32_t ckdiv = 0;
uint32_t c_lh_div;
if (ul_speed > I2C_FAST_MODE_SPEED) {
return FAIL;
}
c_lh_div = ul_mck / (ul_speed * TWI_CLK_DIVIDER) - TWI_CLK_CALC_ARGU;
//cldiv必须适合8位,cldiv必须适合3位
while ((c_lh_div > TWI_CLK_DIV_MAX) && (ckdiv < TWI_CLK_DIV_MIN)) {
//增加时钟分压器
ckdiv++;
//分割cldiv值
c_lh_div /= TWI_CLK_DIVIDER;
}
//设置时钟波形发生器寄存器
p_twi->TWI_CWGR =
TWI_CWGR_CLDIV(c_lh_div) | TWI_CWGR_CHDIV(c_lh_div) |
TWI_CWGR_CKDIV(ckdiv);
return PASS;
}
//测试芯片是否响应给指定的I2C地址
如果找到芯片 返回TWI_SUCCESS,否则返回错误代码
uint32_t twi_probe(Twi *p_twi, uint8_t uc_slave_addr)
{
twi_packet_t packet;
uint8_t data = 0;
//发送数据
packet.buffer = &data;
//数据长度
packet.length = 1;
//从芯片地址
packet.chip = (uint32_t) uc_slave_addr;
//芯片内部地址
packet.addr = 0;
//地址长度
packet.addr_length = 0;
//执行主写访问
return (twi_master_write(p_twi, &packet));
}
//内部构造TWI模块地址寄存器字段
TWI模块地址寄存器先发送MSB,尺寸控制哪一个字节是MSB开始
static uint32_t twi_mk_addr(const uint8_t *addr, int len)
{
uint32_t val;
if (len == 0)
return 0;
val = addr;
if (len > 1) {
val <<= 8;
val |= addr;
}
if (len > 2) {
val <<= 8;
val |= addr;
}
return val;
}
//从一个TWI兼容的从设备读取多个字节
在所有数据被读取或出现错误之前,此函数不会返回
如果读取了所有字节,返回TWI_SUCCESS,否则返回错误代码
uint32_t twi_master_read(Twi *p_twi, twi_packet_t *p_packet)
{
uint32_t status, cnt = p_packet->length;
uint8_t *buffer = p_packet->buffer;
//检查参数
if (cnt == 0) {
return TWI_INVALID_ARGUMENT;
}
//设置读取模式,从地址和3个内部地址字节长度
p_twi->TWI_MMR = 0;
p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(p_packet->chip) |
((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) &
TWI_MMR_IADRSZ_Msk);
//设置远程芯片的内部地址
p_twi->TWI_IADR = 0;
p_twi->TWI_IADR = twi_mk_addr(p_packet->addr, p_packet->addr_length);
//发送一个开始条件
p_twi->TWI_CR = TWI_CR_START;
while (cnt > 0) {
status = p_twi->TWI_SR;
if (status & TWI_SR_NACK) {
return TWI_RECEIVE_NACK;
}
//判断是否是最后一个字节
if (cnt == 1) {
p_twi->TWI_CR = TWI_CR_STOP;
}
if (!(status & TWI_SR_RXRDY)) {
continue;
}
*buffer++ = p_twi->TWI_RHR;
cnt--;
}
while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {
}
p_twi->TWI_SR;
return TWI_SUCCESS;
}
//将多个字节写入一个TWI兼容的从设备
uint32_t twi_master_write(Twi *p_twi, twi_packet_t *p_packet)
{
uint32_t status, cnt = p_packet->length;
uint8_t *buffer = p_packet->buffer;
//检查参数
if (cnt == 0) {
return TWI_INVALID_ARGUMENT;
}
//设置写入模式,从地址和3个内部地址字节长度
p_twi->TWI_MMR = 0;
p_twi->TWI_MMR = TWI_MMR_DADR(p_packet->chip) |
((p_packet->addr_length << TWI_MMR_IADRSZ_Pos) &
TWI_MMR_IADRSZ_Msk);
//设置远程芯片的内部地址
p_twi->TWI_IADR = 0;
p_twi->TWI_IADR = twi_mk_addr(p_packet->addr, p_packet->addr_length);
//发送所有字节
while (cnt > 0) {
status = p_twi->TWI_SR;
if (status & TWI_SR_NACK) {
return TWI_RECEIVE_NACK;
}
if (!(status & TWI_SR_TXRDY)) {
continue;
}
p_twi->TWI_THR = *buffer++;
cnt--;
}
while (1) {
status = p_twi->TWI_SR;
if (status & TWI_SR_NACK) {
return TWI_RECEIVE_NACK;
}
if (status & TWI_SR_TXRDY) {
break;
}
}
p_twi->TWI_CR = TWI_CR_STOP;
while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {
}
return TWI_SUCCESS;
}
//使能TWI中断
void twi_enable_interrupt(Twi *p_twi, uint32_t ul_sources)
{
//启用指定的中断
p_twi->TWI_IER = ul_sources;
}
//关闭TWI中断
void twi_disable_interrupt(Twi *p_twi, uint32_t ul_sources)
{
//关闭指定的中断
p_twi->TWI_IDR = ul_sources;
//伪读
p_twi->TWI_SR;
}
//得到TWI中断状态
uint32_t twi_get_interrupt_status(Twi *p_twi)
{
return p_twi->TWI_SR;
}
//读TWI中断掩码
uint32_t twi_get_interrupt_mask(Twi *p_twi)
{
return p_twi->TWI_IMR;
}
//从TWI总线读取一个字节
uint8_t twi_read_byte(Twi *p_twi)
{
return p_twi->TWI_RHR;
}
//向总线上的一个TWI从服务器发送一个字节的数据
void twi_write_byte(Twi *p_twi, uint8_t uc_byte)
{
p_twi->TWI_THR = uc_byte;
}
//使能TWI从模式
void twi_enable_slave_mode(Twi *p_twi)
{
//设置主禁用位和从禁用位
p_twi->TWI_CR = TWI_CR_MSDIS;
p_twi->TWI_CR = TWI_CR_SVDIS;
//设置主使能位
p_twi->TWI_CR = TWI_CR_SVEN;
}
//关闭TWI从模式
void twi_disable_slave_mode(Twi *p_twi)
{
//设置从机禁用位
p_twi->TWI_CR = TWI_CR_SVDIS;
}
//初始化TWI从模式
void twi_slave_init(Twi *p_twi, uint32_t ul_device_addr)
{
//禁用TWI中断
p_twi->TWI_IDR = ~0UL;
p_twi->TWI_SR;
//复位TWI
twi_reset(p_twi);
//在从模式下设置从地址
p_twi->TWI_SMR = TWI_SMR_SADR(ul_device_addr);
//使能从模式
twi_enable_slave_mode(p_twi);
}
//设置TWI从地址
void twi_set_slave_addr(Twi *p_twi, uint32_t ul_device_addr)
{
//设置从地址
p_twi->TWI_SMR = TWI_SMR_SADR(ul_device_addr);
}
//从主服务器读取数据
uint32_t twi_slave_read(Twi *p_twi, uint8_t *p_data)
{
uint32_t status, cnt = 0;
do {
status = p_twi->TWI_SR;
if (status & TWI_SR_SVACC) {
if (!(status & TWI_SR_GACC) &&
((status & (TWI_SR_SVREAD | TWI_SR_RXRDY))
== (TWI_SR_SVREAD | TWI_SR_RXRDY))) {
*p_data++ = (uint8_t) p_twi->TWI_RHR;
cnt++;
}
} else if ((status & (TWI_SR_EOSACC | TWI_SR_TXCOMP))
== (TWI_SR_EOSACC | TWI_SR_TXCOMP)) {
break;
}
} while (1);
return cnt;
}
//将数据写入TWI总线
uint32_t twi_slave_write(Twi *p_twi, uint8_t *p_data)
{
uint32_t status, cnt = 0;
do {
status = p_twi->TWI_SR;
if (status & TWI_SR_SVACC) {
if (!(status & (TWI_SR_GACC | TWI_SR_SVREAD)) &&
(status & TWI_SR_TXRDY)) {
p_twi->TWI_THR = *p_data++;
cnt++;
}
} else if ((status & (TWI_SR_EOSACC | TWI_SR_TXCOMP))
== (TWI_SR_EOSACC | TWI_SR_TXCOMP)) {
break;
}
} while (1);
return cnt;
}
//重置TWI
void twi_reset(Twi *p_twi)
{
//设置SWRST位重置TWI外设
p_twi->TWI_CR = TWI_CR_SWRST;
p_twi->TWI_RHR;
}
//获取TWI PDC基址
Pdc *twi_get_pdc_base(Twi *p_twi)
{
Pdc *p_pdc_base = NULL;
if (p_twi == TWI0) {
p_pdc_base = PDC_TWI0;
}
#ifdef PDC_TWI1
else if (p_twi == TWI1) {
p_pdc_base = PDC_TWI1;
}
#endif
#ifdef PDC_TWI2
else if (p_twi == TWI2) {
p_pdc_base = PDC_TWI2;
}
#endif
else
{
Assert(false);
}
return p_pdc_base;
}
#if SAM4E
//启用\禁用写保护模式
void twi_set_write_protection(Twi *p_twi, bool flag)
{
if (flag) {
p_twi->TWI_WPROT_MODE = TWI_WP_KEY_VALUE | TWI_WPROT_MODE_WPROT;
} else {
p_twi->TWI_WPROT_MODE = TWI_WP_KEY_VALUE;
}
}
//简要读取写入保护状态
void twi_read_write_protection_status(Twi *p_twi, uint32_t *p_status)
{
*p_status = p_twi->TWI_WPROT_STATUS;
}
#endif
页:
[1]