Mcuzone_TKN 发表于 2020-5-14 13:53:49

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) ;

}

Mcuzone_TKN 发表于 2020-5-14 13:57:40

初始化
#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


Mcuzone_TKN 发表于 2020-5-14 14:28:01

//使能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]
查看完整版本: SAM4E-EK开发板代码解读11——EEPROM