/* 93C46选默认的16位模式,但SPI总线上每次发送/接收8位数据 */
#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=>PB9if (data & 0x80)GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=>PB7elseGPIOB->BRR = GPIO_BRR_BR7;data <<= 1;GPIOB->BSRR = GPIO_BSRR_BS9;}
}void par_out(void)
{GPIOB->BRR = GPIO_BRR_BR8; // RCLK=>PB8GPIOB->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手册的时序图Figure 2, 虽然发送数据没有问题, 但接收数据时,  SCK上升沿后需要等待tPD0或tPD1的时间后本位的数据才会出现在DO上// 如果上升沿出现时就抓取数据, 那么读到的不是本位的数据,而是上一位的数据// 因此,我们接收到的数据都是右移了一位之后的数据uint16_t data = 0;uint16_t temp;// 开始GPIOA->BRR = GPIO_BRR_BR3; // CS=0GPIOA->BSRR = GPIO_BSRR_BS3; // CS=1SPI1->CR1 |= SPI_CR1_SPE; // 启用SPISPI1->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=0while (SPI1->SR & SPI_SR_BSY);SPI1->CR1 &= ~SPI_CR1_SPE; // 关闭SPIdelay();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,SCK、MOSI应配置为复用推挽输出(b),而MISO应配置为带上拉输入(8)GPIOA->CRL = 0xb8b03000;GPIOA->BSRR = GPIO_BSRR_BS6; // 带上拉输入// 数码管动态扫描端口PB7~PB9GPIOB->CRH = 0x00000033;GPIOB->CRL = 0x30000000;SPI1->CR1 |= SPI_CR1_MSTR; // 设为主模式// SPI1->CR1 &= ~SPI_CR1_DFF; // 每次传送的数据位数为8位(DFF=0)SPI1->CR1 |= SPI_CR1_BR; // BR=111, 选256分频// SPI1->CR2 &= ~SPI_CR2_SSOE; // 不使用NSS(=PA4)端口。因为该端口的有效电平是低电平, 而93C46的有效片选信号为高电平SPI1->CR1 |= SPI_CR1_SSM; // 使用软件管理NSS端口,PA4可用作普通I/O口SPI1->CR1 |= SPI_CR1_SSI; // 设置NSS的状态: 已选中while (1){num = _93C46_Read(id);for (i = 0; i < 50; i++)seg_scan();id++;if (id > 63)id = 0;}
}

本文所用的单片机型号:STM32F103C8T6 存储器型号:ST93C46

电源电压:3.2V

8位数码管由两片74HC595驱动(见ser_in,par_out函数)。

/* 93C46选默认的16位模式,SPI每次也发送16位数据 */
#include <stm32f10x.h>#define _BV(n) (1 << (n))
#define CS_0 (GPIOA->BRR = GPIO_BRR_BR3)
#define CS_1 (GPIOA->BSRR = GPIO_BSRR_BS3)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=>PB9if (data & 0x80)GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=>PB7elseGPIOB->BRR = GPIO_BRR_BR7;data <<= 1;GPIOB->BSRR = GPIO_BSRR_BS9;}
}void par_out(void)
{GPIOB->BRR = GPIO_BRR_BR8; // RCLK=>PB8GPIOB->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手册的时序图Figure 2, 虽然发送数据没有问题, 但接收数据时,  SCK上升沿后需要等待tPD0或tPD1的时间后本位的数据才会出现在DO上// 如果上升沿出现时就抓取数据, 那么读到的不是本位的数据,而是上一位的数据// 因此,我们接收到的数据都是右移了一位之后的数据uint16_t data = 0;uint16_t temp;// 开始CS_0;CS_1;SPI1->CR1 |= SPI_CR1_SPE; // 启用SPISPI1->DR = 0xc000 | ((addr & 0x3f) << 7); // 发送操作码(110)、地址码 (1)while ((SPI1->SR & SPI_SR_TXE) == 0); // 注意: TXE=1并不代表当前字节发送完毕, 有可能只发送了一两个字节SPI1->DR = 0x0000; // 送入下次要发的内容: 根据器件手册上的时序图, 地址发送完毕后应发送0x0000, 即DI一直为低电平,不是什么都不发 (2)while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待接收数据temp = SPI1->DR; // 收到的数据: 第15~7位全为1(从器件发送的高阻态被视为1), 第6位为0(dummy bit, 空白位), 第5~0位为所读取数据的第15~10位// 数据的发送和接收是同时进行的// 只有当前字节发送完毕了, RXNE才置位, 而TXE早就置位了(参阅手册上的Figure 240)// RXNE置位表明(1)已发送完毕, 开始发送(2)data = (temp & 0x3f) << 10; // 去掉第15~6位后送入data变量while ((SPI1->SR & SPI_SR_TXE) == 0);while ((SPI1->SR & SPI_SR_RXNE) == 0); // 等待(2)发送完毕temp = SPI1->DR; // 第9~0位数据data |= temp >> 6;// 结束CS_0;while (SPI1->SR & SPI_SR_BSY);SPI1->CR1 &= ~SPI_CR1_SPE; // 关闭SPIreturn 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,SCK、MOSI应配置为复用推挽输出(b),而MISO应配置为带上拉输入(8)GPIOA->CRL = 0xb8b03000;GPIOA->BSRR = GPIO_BSRR_BS6; // 带上拉输入// 数码管动态扫描端口PB7~PB9GPIOB->CRH = 0x00000033;GPIOB->CRL = 0x30000000;SPI1->CR1 |= SPI_CR1_MSTR; // 设为主模式SPI1->CR1 |= SPI_CR1_DFF; // 每次传送的数据位数为16位(DFF=1)SPI1->CR1 |= SPI_CR1_BR; // BR=111, 选256分频// SPI1->CR2 &= ~SPI_CR2_SSOE; // 不使用NSS(=PA4)端口。因为该端口的有效电平是低电平, 而93C46的有效片选信号为高电平SPI1->CR1 |= SPI_CR1_SSM; // 使用软件管理NSS端口,PA4可用作普通I/O口SPI1->CR1 |= SPI_CR1_SSI; // 设置NSS的状态: 已选中while (1){num = _93C46_Read(id);for (i = 0; i < 50; i++)seg_scan();id++;if (id > 63)id = 0;}
}

【程序】STM32使用SPI接口读取93C46存储器上的数据(非软件模拟SPI时序)相关推荐

  1. 【程序】STM32使用SPI接口操作93C46存储器(非软件模拟)

    /* 93C46选默认的16位模式 */ #include <stm32f10x.h>#define _BV(n) (1 << (n)) #define CS_0 (GPIOA ...

  2. 用GPIO模拟SPI接口读取传感器数据

    本文基于平头哥开发板RVB2601,简要介绍了用GPIO模拟SPI时序逻辑,实现SPI协议,按照特定温度传感器的时序,读取其数据,及示例程序 一.概述 SPI(Serial Peripheral In ...

  3. java如何编写spi接口_软件模拟SPI接口程序代码(4种模式)

    软件模拟SPI接口程序代码(4种模式) SPI协议简介 SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说 ...

  4. 软件模拟SPI接口程序代码(4种模式)

    软件模拟SPI接口程序代码(4种模式) SPI协议简介 SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说 ...

  5. 单片机软件模拟SPI接口—加深理解SPI总线协议

    单片机软件模拟SPI接口-加深理解SPI总线协议   SPI(Serial Peripheral Interfacer 串行外设接口)是摩托罗拉公司推出的一种同步串行通讯接口,用于微处理器臌控制器和外 ...

  6. STM32F103软件模拟SPI接口驱动ILI9486液晶屏

    STM32F103软件模拟SPI接口驱动ILI9486液晶屏 ILI9486的工作模式 ILI9486的SPI总线方式简介 ILI9486的3线SPI总线底层驱动配置步骤 ILI9486的工作模式 I ...

  7. 【STM32】0.96寸OLED显示屏(7针SPI协议)软件模拟SPI

    Author:AXYZdong 自动化专业 工科男 有一点思考,有一点想法,有一点理性 [自制展示]2020鼠年大吉 文章目录 概述 3.1硬件设计 3.2软件设计 3.2.1编程要点 3.2.2代码 ...

  8. 关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)

    趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议.SPI协议.I2C协议.RS485协议.大概半年前,是过了一遍的,但也只停留于读了遍代码,跑了下例程,最近又过了一遍( ...

  9. 一文搞懂——软件模拟SPI

    关于stm32通信协议:软件模拟SPI.软件模拟I2C的总结(fishing_8)_To_be_a_fisher的博客-CSDN博客_stm32 软件spi 发现一篇写的软件模拟SPI的比较容易理解的 ...

最新文章

  1. 【Unity】12.5 Navmesh Obstacle组件
  2. Ubuntu下安装DEB包时出现错误的解救方法
  3. 基于Swoole和Redis实现的并发队列处理系统
  4. 微信小程序根据坐标点解析地址常见报错:请求来源未被授权
  5. 【转】C++调用Matlab的.m文件
  6. Java设计模式笔记(4)模版方法模式
  7. java中stack集合框架
  8. Git笔记(31) 重置揭密
  9. 机器学习分类_机器学习之简单分类模型
  10. xtragrid 某个值 查找_Java 经典算法:二分法查找(循环和递归两种方式实现)
  11. 复合页( Compound Page )
  12. 唯一的确定一棵二叉树
  13. Java 继承 小练习
  14. 利用FbinstTool+大白菜u盘工具,制作多系统启动U盘【转】
  15. 面试IT公司的时候,程序员的简历应该写多少个项目经验比较合适?
  16. Excel删除重复值
  17. python导入openpyxl_python系列之(6)Python使用Openpyxl操作Excel
  18. 'Publishing' to tomcat v8.0 server at localhos... Tomcat 报错
  19. 二胎政策来袭 职场妈妈生还是升
  20. cartographer中的pose_extrapolator梳理

热门文章

  1. 安卓开发就等同于Java编程么?不懂的进来看看吧
  2. vmware搭建多台虚拟机-桥接模式
  3. linux slub分配器,slub分配器
  4. win XP实现自动登陆
  5. 纪中DAY5做题小结
  6. Python+PyCharm+PyTorch+Cuda/GPU 安装步骤
  7. 国密算法Go语言实现(详解)(九) ——SM2(椭圆曲线公钥密码算法)
  8. 关于使用腾讯云HiFlow场景连接器每天提醒签到打卡
  9. 欧几里得算法(扩展欧几里得、欧拉定理、费马小定理)
  10. 嘉立创EDA专业版--文件名称修改、原理图尺寸修改与文本放置