发新帖

[ST] STM32单片机操作ST93C46存储器的程序(3.2V电压)

admin 2月前 83

#include <stm32f10x.h>  
  
#define _BV(n) (1 << (n))  
#define CS_0 (GPIOA->BRR = GPIO_BRR_BR3)  
#define CS_1 (GPIOA->BSRR = GPIO_BSRR_BS3)  
#define SK_0 (GPIOA->BRR = GPIO_BRR_BR5)  
#define SK_1 (GPIOA->BSRR = GPIO_BSRR_BS5)  
#define DI_0 (GPIOA->BRR = GPIO_BRR_BR7)  
#define DI_1 (GPIOA->BSRR = GPIO_BSRR_BS7)  
#define DO ((GPIOA->IDR & GPIO_IDR_IDR6) != 0)  
  
uint16_t num = 0;  
uint8_t nid = 0;  
const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};  
  
void delay(void)  
{  
    uint16_t i;  
    for (i = 0; i < 20000; i++);  
}  
  
void delay_short(void)  
{  
    uint16_t i;  
    for (i = 0; i < 300; i++);  
}  
  
void ser_in(uint8_t data)  
{  
    uint8_t i;  
    for (i = 0; i < 8; i++)  
    {  
        GPIOB->BRR = GPIO_BRR_BR9; // SCLK=>PB9  
        if (data & 0x80)  
            GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=>PB7  
        else  
            GPIOB->BRR = GPIO_BRR_BR7;  
        data <<= 1;  
        GPIOB->BSRR = GPIO_BSRR_BS9;  
    }  
}  
  
void par_out(void)  
{  
    GPIOB->BRR = GPIO_BRR_BR8; // RCLK=>PB8  
    GPIOB->BSRR = GPIO_BSRR_BS8;  
}  
  
void seg_scan(void)  
{  
    uint8_t i;  
    uint32_t n = num;  
    for (i = 0; i <= 4; i++)  
    {  
        ser_in(seg8[n % 10]);  
        ser_in(_BV(i));  
        par_out();  
        delay();  
        n /= 10;  
    }  
      
    n = nid;  
    for (i = 6; i <= 7; i++)  
    {  
        ser_in(seg8[n % 10]);  
        ser_in(_BV(i));  
        par_out();  
        delay();  
        n /= 10;  
    }  
}  
  
void _93C46_WriteOP(uint8_t op)  
{  
    // 初态  
    CS_0;  
    SK_0;  
      
    // 开始位  
    DI_1;  
    CS_1;  
    delay_short(); // tCSS>=0.5us  
    SK_1;  
    delay_short(); // tSKH>=0.25us  
    SK_0;  
    //delay_short(); // tSKL>=0.25us, 由于下面已经有一句延时了, 所以这句可以不要  
      
    // OP位  
    if ((op & 2) == 0) // 操作码第一位  
        DI_0;  
    delay_short(); // 数据在下降沿至少要保持tDIS>=0.1us  
    SK_1;  
    delay_short(); // 数据在上升沿至少要保持tDIH>=0.1us的时间  
    SK_0;  
    if (op & 1) // 改变数据, 发送操作码第二位  
        DI_1;  
    else  
        DI_0;  
    delay_short(); // tDIS, tSKL  
      
    SK_1;  
    delay_short(); // tDIH, tSKH  
    SK_0;  
    delay_short(); // tSKL  
}  
  
void _93C46_WriteAddress(uint8_t addr)  
{  
    uint8_t i;  
    for (i = 0; i < 6; i++)  
    {  
        if (addr & 0x20)  
            DI_1;  
        else  
            DI_0;  
        delay_short(); // tDIS, tSKL  
        SK_1;  
        delay_short(); // tSKH  
        SK_0;  
        addr <<= 1;  
    }  
    delay_short(); // tSKL  
    DI_0;  
}  
  
uint16_t _93C46_ReadWord(void)  
{  
    uint8_t i;  
    uint16_t data = 0;  
    for (i = 0; i < 16; i++)  
    {  
        // SK上升沿出现后tPD<=0.25us,才出现本位要读的数据  
        SK_1;  
        delay_short();  
        data <<= 1;  
        if (DO)  
            data |= 1;  
        SK_0;  
        delay_short();  
    }  
    return data;  
}  
  
uint16_t _93C46_Read(uint8_t addr)  
{  
    uint16_t data;  
    _93C46_WriteOP(2);  
    _93C46_WriteAddress(addr);  
    data = _93C46_ReadWord();  
    CS_0;  
    return data;  
}  
  
void _93C46_WriteWord(uint16_t dat)  
{  
    uint8_t i;  
    for (i = 0; i < 16; i++)  
    {  
        if (dat & 0x8000)  
            DI_1;  
        else  
            DI_0;  
        delay_short(); // tDIS, tSKL  
        SK_1;  
        delay_short(); // tSKH  
        SK_0;  
        dat <<= 1;  
    }  
    delay_short(); // tSKL  
    DI_0;  
}  
  
// 等待操作完毕  
void _93C46_Wait(void)  
{  
    CS_0;  
    CS_1;  
    delay_short(); // tSV<=250ns  
    while (!DO);  
    CS_0;  
}  
  
// 允许/禁止擦写  
void _93C46_EnableWrite(uint8_t enabled)  
{  
    _93C46_WriteOP(0);  
    _93C46_WriteAddress((enabled) ? 0x30 : 0x00);  
    CS_0;  
}  
  
// 写入单个存储单元  
void _93C46_Write(uint8_t addr, uint16_t dat)  
{  
    _93C46_WriteOP(1);  
    _93C46_WriteAddress(addr);  
    _93C46_WriteWord(dat);  
    _93C46_Wait();  
}  
  
// 擦除单个存储单元  
void _93C46_Erase(uint8_t addr)  
{  
    _93C46_WriteOP(3);  
    _93C46_WriteAddress(addr);  
    _93C46_Wait();  
}  
  
// 擦除所有存储单元  
// 经测试,该命令可以在3.2V的电压下完成  
// 本人使用的是ST公司的93C46芯片,和STM32单片机是同一家公司生产的  
void _93C46_EraseAll(void)  
{  
    _93C46_WriteOP(0);  
    _93C46_WriteAddress(0x20);  
    _93C46_Wait();  
}  
  
// 将所有的存储单元设为指定值  
// 同样也可以在3.2V的电压下完成  
void _93C46_WriteAll(uint16_t dat)  
{  
    _93C46_WriteOP(0);  
    _93C46_WriteAddress(0x10);  
    _93C46_WriteWord(dat);  
    _93C46_Wait();  
}  
  
int main(void)  
{  
    uint8_t i;  
      
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN;  
    GPIOA->CRL = 0x38303000;  
    GPIOA->BSRR = GPIO_BSRR_BS6;  
    GPIOB->CRH = 0x00000033;  
    GPIOB->CRL = 0x30000000;  
      
    /* 
    _93C46_EnableWrite(1); 
    for (i = 0; i < 64; i++) 
        _93C46_Write(i, 40000 + i * 100 + i); 
    _93C46_EnableWrite(0); 
    */  
      
    while (1)  
    {  
        num = _93C46_Read(nid);  
        for (i = 0; i < 50; i++)  
            seg_scan();  
          
        nid++;  
        if (nid > 63)  
            nid = 0;  
    }  
}

注意:J-Link只能提供3.2V的电压,如果不接USB线,板上的5V输出端只能输出2.6V的电压

这时只需要再插上USB线(无需拔掉J-Link),就能提供4.8V的电压

发送数据时,数据需要在SK=0时准备好,且保持tDIS>=0.1us的时间后才允许拉高SK。

在SK高电平期间数据还需保持tDIH>=0.1us才能释放。

接收数据时,SK上升沿后经过tPD0=tPD1<=0.25us出现当前位的数据,因此可以在SK下降沿期间对数据进行采样。

等待擦写操作完毕时,先将片选信号CS拉低,经过时间tCS>=0.25us后拉高,等待tSV<=0.25us,检测DO的状态。若DO=0,表明写入还未完成。若DO=1,则操作已完毕。此时拉低CS,经过tDF<=0.1us的时间后DO变回高阻态(若单片机输入端开了上拉电阻,则变回高电平)。

数据发送后需要经过tWP<=10ms的时间才能写入完毕。当电源电压为4.5~5.5V时,通常需要3ms,最短时间为0.1ms。


最新回复 (0)
返回