作者共发了5篇帖子。 内容转换:不转换▼
 
点击 回复
741 4
STM32的SPI虽然只能发送8或16位的数据,但也能用来操作93C46!
一派护法 十九级
1楼 发表于:2017-2-22 23:16
void _93C46_WriteEnable(void)
{
    // 开始
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
   
    // 发送操作码及地址码(=0x30)前5位
    SPI1->DR = 0x98;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 发送地址码末位
    SPI1->DR = 0x00;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    delay();
}

void _93C46_Write(void)
{
    // 开始
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
   
    // 发送操作码及地址码前5位
    SPI1->DR = 0xa0;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 发送地址码末位和前7位数据
    SPI1->DR = 0x1e;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 发送中间8位数据
    SPI1->DR = 0x10;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 发送最后一位数据
    SPI1->DR = 0x00;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
   
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
   
    delay();
}

int main(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_SPI1EN;
   
    // CS(1)接PA3, SCK=PA5接SK(2), MISO=PA6接DO(4), MOSI=PA7接DI(3), ORG悬空选16位模式
    // 根据参考手册RM0008_166页的Table25,NSS、SCK、MOSI应配置为复用推挽输出(b),而MISO应配置为带上拉输入(8)
    GPIOA->CRL = 0xb8bb3000;
    GPIOA->BSRR = GPIO_BSRR_BS6; // 带上拉输入
   
    // 数码管动态扫描端口PB7~PB9
    GPIOB->CRH = 0x00000033;
    GPIOB->CRL = 0x30000000;
   
    SPI1->CR1 |= SPI_CR1_MSTR; // 设为主模式
    SPI1->CR1 &= ~SPI_CR1_DFF; // 位数为8位
    SPI1->CR1 |= SPI_CR1_BR; // BR=111, 选256分频
    SPI1->CR2 |= SPI_CR2_SSOE; // 启用NSS端口
    SPI1->CR1 |= SPI_CR1_SPE; // 启用SPI
   
    _93C46_WriteEnable();
    _93C46_Write();
    num = _93C46_Read(0);
   
    while (1)
        seg_scan();
}


其中_93C46_Write函数成功地在0号地址写入了15392这个数!
发送的数据是0xa0, 0x1e, 0x10, 0x00
一派护法 十九级
2楼 发表于:2017-2-22 23:20
写操作的操作码为101,地址前5位为00000,合起来10100000=0xa0就是第一个要发送的数据
地址码末位为0,data=15392=0011110000100000,前7位为0011110,合起来刚好是00011110=0x1e,这是第二个要发送的数据
data的中间8位是00010000=0x10,这是第三个要发送的数据
末位为0,再添上7个无效的0,合起来就是第四个要发送的数据:0x00
一派护法 十九级
3楼 发表于:2017-2-23 10:10
以下程序以及可以读写地址[0], [1]处的内容了,但只能读不能写[20]处的内容。

#include <stm32f10x.h>

#define _BV(n) (1 << (n))

uint16_t num = 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 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;
    }
}

uint16_t _93C46_Read(uint8_t addr)
{
    // 开始
    uint16_t data = 0;
    uint16_t temp;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
    
    // 发送操作码及地址码前5位
    SPI1->DR = 0xc0 + ((data >> 1) & 0x1f);
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待接收数据
    temp = SPI1->DR; // 忽略这次读取的数据
    
    // 发送地址码末位,并接收前6位数据
    SPI1->DR = (addr << 7) & 0x80;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    while ((SPI1->SR & SPI_SR_RXNE) == 0);
    temp = SPI1->DR;
    data = (temp & 0x3f) << 10;
    
    // 发送0x00(目的是为了产生接收数据所需的时钟), 接收中间8位数据
    SPI1->DR = 0x00;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    while ((SPI1->SR & SPI_SR_RXNE) == 0);
    temp = SPI1->DR;
    data |= temp << 2;
    
    // 发送0x00, 接收最后两位数据
    SPI1->DR = 0x00;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    while ((SPI1->SR & SPI_SR_RXNE) == 0);
    temp = SPI1->DR;
    data |= temp >> 6;
    
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    delay();
    return data;
}

void _93C46_WriteEnable(uint8_t enabled)
{
    // 开始
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
    
    // 发送操作码(100)及地址码(=0x30)或(=0x00)
    SPI1->DR = (enabled) ? 0x98 : 0x80;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    SPI1->DR = 0x00;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    delay();
}

void _93C46_Write(uint8_t addr, uint16_t data)
{
    // 开始
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
    
    // 发送操作码(101)、地址码以及前7位数据
    SPI1->DR = (0xa0 | (addr >> 1)) & 0xff;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    SPI1->DR = ((addr << 7) | (data >> 9)) & 0xff;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    // 发送后9位数据
    SPI1->DR = (data >> 1) & 0xff;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    SPI1->DR = (data << 7) & 0x80;
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    delay();
}

int main(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_SPI1EN;
    
    // CS(1)接PA3, SCK=PA5接SK(2), MISO=PA6接DO(4), MOSI=PA7接DI(3), ORG悬空选16位模式
    // 根据参考手册RM0008_166页的Table25,NSS、SCK、MOSI应配置为复用推挽输出(b),而MISO应配置为带上拉输入(8)
    GPIOA->CRL = 0xb8bb3000;
    GPIOA->BSRR = GPIO_BSRR_BS6; // 带上拉输入
    
    // 数码管动态扫描端口PB7~PB9
    GPIOB->CRH = 0x00000033;
    GPIOB->CRL = 0x30000000;
    
    SPI1->CR1 |= SPI_CR1_MSTR; // 设为主模式
    //SPI1->CR1 |= SPI_CR1_DFF; // 位数为16位
    SPI1->CR1 |= SPI_CR1_BR; // BR=111, 选256分频
    SPI1->CR2 |= SPI_CR2_SSOE; // 启用NSS端口
    SPI1->CR1 |= SPI_CR1_SPE; // 启用SPI
    
    num = _93C46_Read(0);
    _93C46_WriteEnable(1);
    _93C46_Write(0, num + 1);

    while (1)
        seg_scan();
}
一派护法 十九级
4楼 发表于:2017-2-23 23:06
【终于解决读的问题了!原来是第一个发送的数据中把addr写成了data了!】
#include <stm32f10x.h>

#define _BV(n) (1 << (n))

uint8_t id = 0;
uint16_t num = 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 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 = id;
    for (i = 6; i <= 7; i++)
    {
        ser_in(seg8[n % 10]);
        ser_in(_BV(i));
        par_out();
        delay();
        n /= 10;
    }
}

uint16_t _93C46_Read(uint8_t addr)
{
    // SPI中我们配置的是CPOL=0, 即SCK的空闲状态为低电平; CPHA=0, 也就是在SCK的上升沿对数据进行采样
    // 这里会产生一个问题: 根据EEPROM手册, 发送数据倒是没有问题, 但接收数据时,  SCK上升沿后需要等待tPD0=tPD1后数据才会出现
    // 如果上升沿出现时就抓取数据, 那么读到的不是本位的数据,而是上一位的数据
    // 因此,我们接收到的数据都是右移了一位之后的数据
    uint16_t data = 0;
    uint16_t temp;
   
    // 开始
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1
   
    SPI1->CR1 |= SPI_CR1_SPE; // 启用SPI
    SPI1->DR = 0xc0 + ((addr >> 1) & 0x1f); // 发送操作码及地址码前5位 (1)
    while ((SPI1->SR & SPI_SR_TXE) == 0); // 注意: TXE=1并不代表当前字节发送完毕, 有可能只发送了一两个字节
    SPI1->DR = (addr << 7) & 0x80; // 送入下次要发送的内容: 地址码末位 (2)
    while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待接收数据
    temp = SPI1->DR; // 忽略这次读取的数据, 因为收到的数据恒为0xff
    // 数据的发送和接收是同时进行的
    // 只有当前字节发送完毕了, RXNE才置位, 而TXE早就置位了(参阅手册上的Figure 240)
    // RXNE置位表明(1)已发送完毕, 开始发送(2)
   
    while ((SPI1->SR & SPI_SR_TXE) == 0); // TXE置位后才能放入新数据, 此时(2)还未发送完毕
    SPI1->DR = 0x00; // 送入下次要发的内容: 根据器件手册上的时序图, 地址发送完毕后应发送0x00, 即DI一直为低电平,不是什么都不发 (3)
    while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待(2)发送完毕
    temp = SPI1->DR; // 收到的数据: 最高位为1(从器件发送的高阻态被视为1), 次高位为0(dummy bit, 空白位), 低6位为所读取数据的第15~10位
    data = (temp & 0x3f) << 10; // 去掉高两位后送入data变量
   
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    SPI1->DR = 0x00; // 送入最后一次要发送的内容 (4)
    while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待(3)发送完毕
    temp = SPI1->DR; // 第9~2位数据
    data |= temp << 2;
   
    while ((SPI1->SR & SPI_SR_TXE) == 0);
    while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待(4)发送完毕
    temp = SPI1->DR;
    data |= temp >> 6;
   
    // 结束
    GPIOA->BRR = GPIO_BRR_BR3; // CS=0
    while (SPI1->SR & SPI_SR_BSY);
    SPI1->CR1 &= ~SPI_CR1_SPE; // 关闭SPI
    delay();
    return data;
}

int main(void)
{
    uint8_t i;
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_SPI1EN;
   
    // CS(1)接PA3, SCK=PA5接SK(2), MISO=PA6接DO(4), MOSI=PA7接DI(3), ORG悬空选16位模式
    // 根据参考手册RM0008_166页的Table25,NSS、SCK、MOSI应配置为复用推挽输出(b),而MISO应配置为带上拉输入(8)
    GPIOA->CRL = 0xb8bb3000;
    GPIOA->BSRR = GPIO_BSRR_BS6; // 带上拉输入
   
    // 数码管动态扫描端口PB7~PB9
    GPIOB->CRH = 0x00000033;
    GPIOB->CRL = 0x30000000;
   
    SPI1->CR1 |= SPI_CR1_MSTR; // 设为主模式
    //SPI1->CR1 |= SPI_CR1_DFF; // 位数为16位
    SPI1->CR1 |= SPI_CR1_BR; // BR=111, 选256分频
    SPI1->CR2 |= SPI_CR2_SSOE; // 启用NSS端口
   
    while (1)
    {
        num = _93C46_Read(id);
        for (i = 0; i < 50; i++)
            seg_scan();
        id++;
        if (id > 63)
            id = 0;
    }
}
一派护法 十九级
5楼 发表于:2017-2-23 23:09
之前写的是:
// 发送操作码及地址码前5位
SPI1->DR = 0xc0 + ((data >> 1) & 0x1f);
而data的值恒为0,太粗心了!

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:741 回复数:4
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2017-2-23 23:09
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。