ZYNQ PS 基于SPI的数码管显示

REVIEW

之前在学习ZYNQ的PL端,已经学习过:

数码管动态扫描显示-优快云博客

SPI接口的74HC595驱动数码管实现_74hc595 spi-优快云博客

对SPI有一定了解哩:

SPI(Serial Peripheral Interface),串行外围设备接口。

SPI是一个同步的数据总线,用单独的数据线一个单独的时钟信号来保证发送端和接收端的同步

SPI 使用 4 根标准信号线进行通信:
                MISO(主输入- 从输出 )
                MOSI(主输出-从输入 )
                时钟 SCLK
                从机选择信号 SS(有时也称为片选信号 CS

1. 今日摸鱼任务

实现ZYNQ PS侧SPI控制数码管显示

小梅哥教材:

03_【裸机教程】基于C编程的Zynq裸机程序设计与应用教程v2.4.5.pdf

第十章 基于SPI控制器的数码管基础应用

ILA遇到了困难,下一次搞一下T_T

2. ZYNQ PS SPI

ZYNQ-7000 PS 侧有两个功能一样的 SPI 外设控制器,其功能结构如图:

APB 接口: 32 位,用于响应寄存器的读、写,处理数据端口和 FIFO 之间的读写命令和数据。
                   数据端口以字节(即[7:0])为单位。
SPI 主机:此时控制器要驱动 SCLK ,并输出 3 个从机选择信号。
                  MOSI 信号上的从机选择和传输开始,可以在软件中手动控制,也可以由硬件自动控制。
SPI 从机:此时控制器只使用一个从机选择的输入信号( SS0 )。
                  SCLK 与控制器的参考时钟(SPI_Ref_Clk )同步。
Tx Rx FIFO :每个 FIFO 都是 128 字节,软件使用寄存器映射后的数据端口寄存器来读、写 FIFO
                           FIFO 桥接了两个时钟域:APB 接口和控制器的SPI_Ref_Clk

3. Block Design

使用EDA拓展版,本次使用EMIO

74HC595 :

SCLK  ---  SCLK/SHCP ---  E19

MOSI  ---  DS  ---------------  D20

SS  ---  RCLK/STCP ---  F17

4. PS_SPI

PS_SPI.h

#ifndef __PS_SPI_H__
#define __PS_SPI_H__

#include "xspips.h"


extern XSpiPs SPI0;


void PS_SPI_Init(XSpiPs *InstancePtr,uint16_t DeviceId, uint8_t Prescaler, uint8_t SPI_Mode);
void PS_SPI_Transfer(XSpiPs *InstancePtr, uint32_t Sel_Num, uint8_t *ReadBuffer, uint8_t *WriteBuffer, uint32_t BUFFER_SIZE);

#endif

PS_SPI.c

#include "PS_SPI.h"


XSpiPs SPI0;


/************************************************************************************************************
**  @brief    初始化PS_SPI设备
**  @param    SpiInstancePtr    SPI对象
**  @param    SpiDeviceId        SPI设备ID
**  @param    SPI_Mode        SPI模式选择
**  @param    Prescaler        分频系数xx                XSPIPS_CLK_PRESCALE_xx
**     模式有以下几种:    XSPIPS_MASTER_OPTION            设置为主机(默认为从机)
**                     XSPIPS_CLK_ACTIVE_LOW_OPTION    将时钟极性设为低电平有效(默认为高电平有效)
**                     XSPIPS_CLK_PHASE_1_OPTION        片选信号后,第二个SCK边缘的数据有效(默认为第一个边缘有效)
**                     XSPIPS_DECODE_SSELECT_OPTION    设置为8位从机模式,使用3位2进制数来选择从机(默认为3位从机模式)
**                     XSPIPS_FORCE_SSELECT_OPTION        手动片选模式
**                     XSPIPS_MANUAL_START_OPTION        设置为手动开始模式
**    Sample:    //初始化SPI设备,设为主机模式,64分频
**            PS_SPI_Init(&SPI0, XPAR_XSPIPS_0_DEVICE_ID,
**                XSPIPS_CLK_PRESCALE_64, XSPIPS_MASTER_OPTION);
************************************************************************************************************/
void PS_SPI_Init(XSpiPs *SpiInstPtr,uint16_t DeviceId, uint8_t Prescaler, uint8_t SPI_Mode)
{
    XSpiPs_Config *SpiConfig;

    //根据设备ID在配置表中查找配置,然后初始化配置
    SpiConfig = XSpiPs_LookupConfig(DeviceId);
    XSpiPs_CfgInitialize(SpiInstPtr, SpiConfig, SpiConfig->BaseAddress);

    //设置SPI的模式
    XSpiPs_SetOptions(SpiInstPtr, SPI_Mode);

    //设置SPI的分频并使能
    XSpiPs_SetClkPrescaler(SpiInstPtr, Prescaler);
    XSpiPs_Enable(SpiInstPtr);
}

/************************************************************************************************************
**    @brief    使用SPI进行主从之间的数据传输
**    @param    SpiInstancePtr    SPI对象
**    @param    Sel_Num            选择从设备的序号,普通模式只能选3个设备(0~2)
**    @param    ReadBuffer        接受的数据所存储的数组
**    @param    WriteBuffer        发送的数据所存储的数组
**    @param    BUFFER_SIZE        接收与发送的数据的字节数
**    Sample:    PS_SPI_Transfer(&SPI0, 0, ReadBuffer, WriteBuffer, 2);//用SPI与0号从机通信,收发2字节
************************************************************************************************************/
void PS_SPI_Transfer(XSpiPs *SpiInstPtr, uint32_t Sel_Num, uint8_t *ReadBuffer, uint8_t *WriteBuffer, uint32_t BUFFER_SIZE)
{
    //选择片选设备,一共3位
    XSpiPs_SetSlaveSelect(SpiInstPtr, Sel_Num);

    //与从机进行数据传输,每发送一个字节就接受一个字节,发送的数据为WriteBuffer里的内容,接收的数据存在ReadBuffer里
    XSpiPs_PolledTransfer(SpiInstPtr, WriteBuffer, ReadBuffer, BUFFER_SIZE);
}
 

5. SCU

SCU_GIC.h

#ifndef SCU_GIC_H_
#define SCU_GIC_H_
#include "xscugic.h"


#define    HIGH_Level_Sensitive    0x01    //高电平敏感(1)
#define    Rising_Edge_Sensitive    0x03    //上升沿敏感(0->1)

XScuGic ScuGic;    //通用中断控制器

void ScuGic_Init();
void Set_ScuGic_Link(uint16_t IntrId, uint8_t Priority, uint8_t Trigger,
        Xil_InterruptHandler Handler, void *CallBackRef);

#endif

SCU_GIC.c

#include "SCU_GIC.h"

XScuGic ScuGic;    //通用中断控制器对象

void ScuGic_Init()
{
    XScuGic_Config *IntcConfig;
    IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
    XScuGic_CfgInitialize(&ScuGic, IntcConfig,IntcConfig->CpuBaseAddress);

    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
    Xil_ExceptionEnable();
}

/**
  *****************************************************
  * @brief    将中断链接到通用中断控制器
  * @param    IntrId        中断请求ID
  * @param    Priority    中断优先级,高5位有效,共32级,步长为8 优先级:0x00 -> 0xf8
  * @param    Trigger        中断触发模式:
  *                             高电平触发 -> HIGH_Level_Sensitive
  *                             上升沿触发 -> Rising_Edge_Sensitive
  * @param    Handler        中断服务函数
  * @param    CallBackRef    回调引用,通常是连接驱动程序的实例指针
  * @usage    //链接XPAR_SCUTIMER_INTR到中断控制器,中断服务函数为ScuTimer_IRQ_Handler
  *         Set_ScuGic_Link(XPAR_SCUTIMER_INTR, 0xA8, HIGH_Level_Sensitive,
  *             ScuTimer_IRQ_Handler, (void *)&ScuTimer);
  *****************************************************
**/
void Set_ScuGic_Link(uint16_t IntrId, uint8_t Priority, uint8_t Trigger,
        Xil_InterruptHandler Handler, void *CallBackRef)
{
    XScuGic_SetPriorityTriggerType(&ScuGic, IntrId, Priority, Trigger);
    XScuGic_Connect(&ScuGic, IntrId,
            (Xil_ExceptionHandler)Handler,CallBackRef);
    XScuGic_Enable(&ScuGic, IntrId);
}

SCU_TIMER.h

#ifndef SCU_TIMER_H_
#define SCU_TIMER_H_

#include "xscutimer.h"
#include <stdint.h>

extern uint8_t Refresh_Flag;

XScuTimer ScuTimer;
#define    CPU_CLK_HZ    XPAR_PS7_CORTEXA9_0_CPU_CLK_FREQ_HZ    //CPU时钟频率(单位Hz)

void ScuTimer_Int_Init(double Load_Val);
void ScuTimer_IRQ_Handler(void *CallBackRef);

#endif

SCU_TIMER.c


#include "SCU_TIMER.h"
#include "SCU_GIC.h"
#include "xparameters.h"
XScuTimer ScuTimer;        //私有定时器

uint8_t Refresh_Flag = 0; //数码管位刷新标志


void ScuTimer_IRQ_Handler(void *CallBackRef)
{
    /* ↓↓↓用户处理↓↓↓ */
    Refresh_Flag = 1;

    /* ↑↑↑结束处理↑↑↑ */
    XScuTimer_ClearInterruptStatus(&ScuTimer);
}


/**
  *****************************************************
  * @brief    初始化私有定时器
  * @param    Time_us        定时器中断触发时间,单位为us
  * @usage    ScuTimer_Int_Init(20);    //初始化私有定时器,每20us触发一次
  * @tag    本函数开启私有定时器,并进行周期性中断,要停止私有定时器可使用XScuTimer_Stop(&ScuTimer);
  *****************************************************
**/
void ScuTimer_Int_Init(double Time_us)
{
    uint32_t Load_Val;
    //定时器初始化
    XScuTimer_Config *Config;
    Config = XScuTimer_LookupConfig(XPAR_XSCUTIMER_0_DEVICE_ID);
    XScuTimer_CfgInitialize(&ScuTimer, Config,Config->BaseAddr);

    //将us转换为定时器装载值
    Load_Val = ((float)CPU_CLK_HZ / 2 / 1000000 * Time_us) - 1;

    //四舍五入,使结果更精准
    if(((uint32_t)(CPU_CLK_HZ / 2 / 100000 * Time_us))%10 >=5 )
        Load_Val++;

    //设置自动装载值和自动装载模式
    XScuTimer_LoadTimer(&ScuTimer, Load_Val);
    XScuTimer_EnableAutoReload(&ScuTimer);

    //链接到中断控制器
    Set_ScuGic_Link(XPAR_SCUTIMER_INTR, 0xA8,Rising_Edge_Sensitive,ScuTimer_IRQ_Handler,(void *)&ScuTimer);

    //使能定时器中断
    XScuTimer_EnableInterrupt(&ScuTimer);

    //开启计数器
    XScuTimer_Start(&ScuTimer);
}

6. main.c

#include <stdio.h>
#include <stdint.h>
#include "xparameters.h"
#include "PS_SPI.h"
#include "SCU_GIC.h"
#include "SCU_TIMER.h"


    // 0 1 2 3 4 5 6 7 8 9 A b C d E F -
const uint8_t data_595[17] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xBF};
    // L - R
const uint8_t seg_8[8]= {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};


int main(void)
{
    //WriteBuffer_init 76543210
    uint8_t WriteBuffer[8][2] = {
                                {data_595[0],seg_8[0]}, //{'0',数码管第 0 位}
                                {data_595[1],seg_8[1]}, //{'1',数码管第 1 位}
                                {data_595[2],seg_8[2]}, //{'2',数码管第 2 位}
                                {data_595[3],seg_8[3]}, //{'3',数码管第 3 位}
                                {data_595[4],seg_8[4]}, //{'4',数码管第 4 位}
                                {data_595[5],seg_8[5]}, //{'5',数码管第 5 位}
                                {data_595[6],seg_8[6]}, //{'6',数码管第 6 位}
                                {data_595[7],seg_8[7]}, //{'7',数码管第 7 位}
                                };

    //数码管位选
    uint8_t Sel_Num = 0;
    //初始化通用中断控制器
    ScuGic_Init();
    //初始化 SPI0,设为主机模式,64 分频
    //CPOL=1,CPHA=1
    PS_SPI_Init(&SPI0, XPAR_XSPIPS_0_DEVICE_ID,    XSPIPS_CLK_PRESCALE_64,
            XSPIPS_MASTER_OPTION |     XSPIPS_CLK_ACTIVE_LOW_OPTION| XSPIPS_CLK_PHASE_1_OPTION |     XSPIPS_FORCE_SSELECT_OPTION);
    //初始化私有定时器中断,定时间隔 1ms
    ScuTimer_Int_Init(1000);
    while(1)
    {

        if(Refresh_Flag)
        {
            //清除数码管刷新标志
            Refresh_Flag = 0;
            //SPI 传输,控制第 Sel_Num 位数码管显示数字
            PS_SPI_Transfer(&SPI0, 0, NULL, WriteBuffer[Sel_Num], 2);
            //位选循环(0~7)
            if(Sel_Num >= 7)
                Sel_Num = 0;
            else
                Sel_Num++;
        }
    }
    return 0;
}
 

//摸鱼结束,ILA有一些奇怪的问题,下次搞~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值