STC8A8K64D4-45M! A12的完美升级版支持DMA,12位ADC...

[复制链接]
6295|18
手机看帖
扫描二维码
随时随地手机跟帖
autopccopy|  楼主 | 2021-5-3 23:14 | 显示全部楼层 |阅读模式
本帖最后由 autopccopy 于 2021-5-3 23:59 编辑

STC8A8K64S4A12 从A版到G版共7版,其中大家接触最多应该是F版。后来STC8G, STC8H系列推出了。现在A12也升级了,主要有:

DMA(批量数据传输),
MDU16( 硬件 16  位乘除法器),
12位数模转换器ADC.
LCM 接口
模拟USB直接下载
精度可达 15  位的增强型PWM


STC8A8K64D4 系列的单片机内部集成了一个 LCM 接口控制器,可用于驱动目前流行的液晶显示屏
模块。可驱动 I8080 接口和 M6800 接口彩屏,支持 8 位和 16 位数据宽度


STC8A8K64D4 系列的单片机内部支持hi 批量数据传输BMM (DMA ),即传统的 DMA 。
支持如下几种 BMM 操作:
 M2M_BMM : XRAM 存储器到 XRAM 存储器的数据读写
 ADC_BMM :自动扫描使能的 ADC 通道并将转换的 ADC 数据自动存储到 XRAM 中
 SPI_BMM :自动将 XRAM 中的数据和 SPI 外设之间进行数据交换
 UR1T_BMM :自动将 XRAM 中的数据通过串口 1 发送出去
 UR1R_BMM :自动将串口 1 接收到的数据存储到 XRAM 中
 UR2T_BMM :自动将 XRAM 中的数据通过串口 2 发送出去
 UR2R_BMM :自动将串口 2 接收到的数据存储到 XRAM 中
 UR3T_BMM :自动将 XRAM 中的数据通过串口 3 发送出去
 UR3R_BMM :自动将串口 3 接收到的数据存储到 XRAM 中
 UR4T_BMM :自动将 XRAM 中的数据通过串口 4 发送出去
 UR4R_BMM :自动将串口 4 接收到的数据存储到 XRAM 中
 LCM_BMM :自动将 XRAM 中的数据和 LCM 设备之间进行数据交换
每次 BMM 数据传输最大数据量为 256 字节。



正在检测目标单片机 ...
  单片机型号: STC8A8K64D4
  固件版本号: 7.4.1U

当前芯片的硬件选项为:
  . 内部振荡器的频率未调节
  . 掉电唤醒定时器的频率: 35.425KHz
  . 振荡器放大增益使能
  . P3.2和P3.3与下次下载无关
  . 上电复位时增加额外的复位延时
  . 复位引脚用作普通I/O口
  . 检测到低压时不复位,可产生低压中断
  . 低压检测门槛电压 : 2.70 V
  . 上电复位时,硬件不启动内部看门狗
  . 上电自动启动内部看门狗时的预分频数为 : 64
  . 空闲状态时看门狗定时器停止计数
  . 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
  . 下次下载用户程序时,将用户EEPROM区一并擦除
  . 下次下载用户程序时,没有相关的端口控制485
  . 下次下载时不需要校验下载口令
  . 内部参考电压: 1185 mV (参考范围: 1100~1300mV)
  . 内部安排测试时间: 2021年4月29日

  单片机型号: STC8A8K64D4
  固件版本号: 7.4.1U

开始调节频率 ...                        [0.922"]
调节后的频率: 11.071MHz (0.109%)

正在重新握手 ... 成功                        [0.125"]
当前的波特率: 115200
正在擦除目标区域 ... 完成 !                [0.672"]
芯片出厂序列号 : F7F4C5D50A8817
正在下载用户代码 ... 完成 !                [0.062"]
正在设置硬件选项 ... 完成 !                [0.016"]

更新后的硬件选项为:
  . 内部IRC振荡器的频率: 11.071MHz
  . 掉电唤醒定时器的频率: 35.425KHz
  . 振荡器放大增益使能
  . P3.2和P3.3与下次下载无关
  . 上电复位时增加额外的复位延时
  . 复位引脚用作普通I/O口
  . 检测到低压时复位
  . 低压检测门槛电压 : 2.00 V
  . 上电复位时,硬件不启动内部看门狗
  . 上电自动启动内部看门狗时的预分频数为 : 256
  . 空闲状态时看门狗定时器停止计数
  . 启动看门狗后,软件可以修改分频数,但不能关闭看门狗
  . 下次下载用户程序时,将用户EEPROM区一并擦除
  . 下次下载用户程序时,没有相关的端口控制485
  . 下次下载时不需要校验下载口令
  . 内部参考电压: 1185 mV (参考范围: 1100~1300mV)
  . 内部安排测试时间: 2021年4月29日
芯片出厂序列号 : F7F4C5D50A8817

  单片机型号: STC8A8K64D4
  固件版本号: 7.4.1U

  . 用户设定频率: 11.059MHz
  . 调节后的频率: 11.071MHz
  . 频率调节误差: 0.109%


操作成功 !(2021-05-03 23:04:07)


STC-ISP-STC8A8K64D4.jpg
STC8A8K64D4-45MHz,DMA,完美 12-BIT ADC.jpg
STC8A8K64D4-45MHz,DMA2.jpg
STC8A8K64D4-45MHz,DMA3.jpg
STC8A8K64D4-45MHz,DMA.jpg
QQ图片20210503234317.jpg
QQ图片20210503234326.jpg
STC8A8K64D4-DMA,完美12位ADC.jpg
STC8A8K64D4与A12管脚相同,无缝升级.jpg

STC8A-STC8F-20210308.pdf

19.74 MB

stc-isp-v6.88E.rar

961.99 KB

使用特权

评论回复

相关帖子

sdwys| | 2021-5-6 12:18 | 显示全部楼层
引脚和各引脚的功能分配和ATC8A8K64S4A12完全兼容吗?完美12位ADC指的是ADC精度提高了吗?

使用特权

评论回复
autopccopy|  楼主 | 2021-5-11 23:04 | 显示全部楼层
本帖最后由 autopccopy 于 2021-5-13 23:58 编辑
sdwys 发表于 2021-5-6 12:18
引脚和各引脚的功能分配和ATC8A8K64S4A12完全兼容吗?完美12位ADC指的是ADC精度提高了吗? ...

从手册看,引脚是PIN2PIN的。升级了DMA等等,详细看手册!附最新的手册及DMA例程包。

(724)
STC8A8K64D4-DMA1.jpg
STC8A8K64D4-DMA2.jpg
STC8A8K64D4-DMA3.jpg
STC8A8K64D4-DMA4S.jpg

210510-STC8A8K64D4数据手册(DMA).pdf

20.39 MB

STC8A8K64D4-DMA-DEMO(210512).ZIP

2.36 MB

使用特权

评论回复
autopccopy|  楼主 | 2021-5-11 23:18 | 显示全部楼层

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.**.com --------------------------------------------*/
/* --- Web: www.***.com  ---------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/

/*************  功能说明    **************

通过串口对PM25LV040/W25X40CL/W25Q80BV进行读写测试。

对FLASH做扇区擦除、写入、读出的操作,命令指定地址。

默认波特率:  115200,8,N,1. 可以在"串口初始化函数"里修改.

串口命令设置: (字母不区分大小写)
    E 0x001234              --> 扇区擦除,指定十六进制地址.
    W 0x001234 1234567890   --> 写入操作,指定十六进制地址,后面为写入内容.
    R 0x001234 10           --> 读出操作,指定十六进制地址,后面为读出字节数.
    C                       --> 如果检测不到PM25LV040/W25X40CL/W25Q80BV, 发送C强制允许操作.

注意:为了通用,程序不识别地址是否有效,用户自己根据具体的型号来决定。

串口写操作的内容放入SPI发送BMM空间,然后启动SPI_BMM进行发送.
读操作的内容通过SPI读取后放在BMM接收空间,由串口进行打印显示.

下载时, 选择时钟 22.1184MHz (用户可自行修改频率).

******************************************/

#include "stdio.h"
#include "stc8a8k64d4.h"

#define     MAIN_Fosc       22118400L   //定义主时钟(精确计算115200波特率)

#define SPIF    0x80        //SPI传输完成标志。写入1清0。
#define WCOL    0x40        //SPI写冲突标志。写入1清0。

#define     Baudrate1           115200L
#define     EE_BUF_LENGTH       255          //

/*************  本地常量声明    **************/

//#define        BMM_TX_ADDR                0x500                /* BMM发送数据存放地址 */
//#define        BMM_RX_ADDR                0x600                /* BMM接收数据存放地址 */

/*************  本地变量声明    **************/
u8  xdata   tmp[EE_BUF_LENGTH];
u8  sst_byte;
u32 Flash_addr;

u8 xdata BmmTxBuffer[256]; //_at_ BMM_TX_ADDR;
u8 xdata BmmRxBuffer[256]; //_at_ BMM_RX_ADDR;

/*************  FLASH相关变量声明   **************/
sbit    P_PM25LV040_CE  = P2^2;     //PIN1
sbit    P_PM25LV040_SO  = P2^4;     //PIN2
sbit    P_PM25LV040_SI  = P2^3;     //PIN5
sbit    P_PM25LV040_SCK = P2^5;     //PIN6

u8  B_FlashOK;                                //Flash状态
u8  PM25LV040_ID, PM25LV040_ID1, PM25LV040_ID2;

#define     UART1_BUF_LENGTH    (EE_BUF_LENGTH+9)   //串口缓冲长度

u8  RX1_TimeOut;
u8  TX1_Cnt;    //发送计数
u8  RX1_Cnt;    //接收计数
bit B_TX1_Busy; //发送忙标志
bit        BmmFlag;

u8  xdata   RX1_Buffer[UART1_BUF_LENGTH];   //接收缓冲

void    delay_ms(u8 ms);
void    RX1_Check(void);
void    UART1_config(u8 brt);   // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
void    UART1_TxByte(u8 dat);

void    SPI_init(void);
void    FlashCheckID(void);
u8      CheckFlashBusy(void);
void    FlashWriteEnable(void);
void    FlashChipErase(void);
void    FlashSectorErase(u32 addr);
void    SPI_Read_Nbytes( u32 addr, u16 size);
u8      SPI_Read_Compare(u16 size);
void    SPI_Write_Nbytes(u32 addr,  u8 size);
void    BMM_Config(void);

// Output a character
void UartPutc(unsigned char my_ch)
{
  SBUF = my_ch;
        B_TX1_Busy = 1;
        while(B_TX1_Busy);
}

char putchar(char c)
{
        UartPutc(c);
        return c;
}

/******************** 主函数 **************************/
void main(void)
{
    P0M1 = 0;   P0M0 = 0;   //设置为准双向口
    P1M1 = 0;   P1M0 = 0;   //设置为准双向口
    P2M1 = 0;   P2M0 = 0;   //设置为准双向口
    P3M1 = 0;   P3M0 = 0;   //设置为准双向口
    P4M1 = 0;   P4M0 = 0;   //设置为准双向口
    P5M1 = 0;   P5M0 = 0;   //设置为准双向口
    P6M1 = 0;   P6M0 = 0;   //设置为准双向口
    P7M1 = 0;   P7M0 = 0;   //设置为准双向口

    delay_ms(10);
    UART1_config(1);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
    BMM_Config();
    EA = 1;     //允许总中断

    printf("命令设置:\r\n");
    printf("E 0x001234            --> 扇区擦除  十六进制地址\r\n");
    printf("W 0x001234 1234567890 --> 写入操作  十六进制地址  写入内容\r\n");
    printf("R 0x001234 10         --> 读出操作  十六进制地址  读出字节数\r\n");
    printf("C                     --> 如果检测不到PM25LV040/W25X40CL/W25Q80BV, 发送C强制允许操作.\r\n\r\n");

    SPI_init();
    FlashCheckID();
    FlashCheckID();
   
    if(!B_FlashOK)  printf("未检测到PM25LV040/W25X40CL/W25Q80BV!\r\n");
    else
    {
        if(B_FlashOK == 1)
        {
            printf("检测到PM25LV040!\r\n");
        }
        else if(B_FlashOK == 2)
        {
            printf("检测到W25X40CL!\r\n");
        }
        else if(B_FlashOK == 3)
        {
            printf("检测到W25Q80BV!\r\n");
        }
        printf("制造商ID1 = 0x%02bX",PM25LV040_ID1);
        printf("\r\n      ID2 = 0x%02bX",PM25LV040_ID2);
        printf("\r\n   设备ID = 0x%02bX\r\n",PM25LV040_ID);
    }

    while (1)
    {
        delay_ms(1);
               
        if(RX1_TimeOut > 0)
        {
            if(--RX1_TimeOut == 0)  //超时,则串口接收结束
            {
                if(RX1_Cnt > 0)
                {
                    RX1_Check();    //串口1处理数据
                }
                RX1_Cnt = 0;
            }
        }
    }
}


//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-3-9
// 备注:
//========================================================================
void delay_ms(u8 ms)
{
     u16 i;
     do{
          i = MAIN_Fosc / 10000;
          while(--i);   //10T per loop
     }while(--ms);
}


/**************** ASCII码转BIN ****************************/
u8  CheckData(u8 dat)
{
    if((dat >= '0') && (dat <= '9'))        return (dat-'0');
    if((dat >= 'A') && (dat <= 'F'))        return (dat-'A'+10);
    return 0xff;
}

/**************** 获取写入地址 ****************************/
u32 GetAddress(void)
{
    u32 address;
    u8  i,j;
   
    address = 0;
    if((RX1_Buffer[2] == '0') && (RX1_Buffer[3] == 'X'))
    {
        for(i=4; i<10; i++)
        {
            j = CheckData(RX1_Buffer[i]);
            if(j >= 0x10)   return 0x80000000;  //error
            address = (address << 4) + j;
        }
        return (address);
    }
    return  0x80000000; //error
}

/**************** 获取要读出数据的字节数 ****************************/
u8  GetDataLength(void)
{
    u8  i;
    u8  length;
   
    length = 0;
    for(i=11; i<RX1_Cnt; i++)
    {
        if(CheckData(RX1_Buffer[i]) >= 10)  break;
        length = length * 10 + CheckData(RX1_Buffer[i]);
    }
    return (length);
}


/**************** 串口1处理函数 ****************************/

void RX1_Check(void)
{
    u8  i,j;

    if((RX1_Cnt == 1) && (RX1_Buffer[0] == 'C'))    //发送C强制允许操作
    {
        B_FlashOK = 1;
        printf("强制允许操作FLASH!\r\n");
    }

    if(!B_FlashOK)
    {
        printf("PM25LV040/W25X40CL/W25Q80BV不存在, 不能操作FLASH!\r\n");
        return;
    }
   
    F0 = 0;
    if((RX1_Cnt >= 10) && (RX1_Buffer[1] == ' '))   //最短命令为10个字节
    {
        for(i=0; i<10; i++)
        {
            if((RX1_Buffer[i] >= 'a') && (RX1_Buffer[i] <= 'z'))    RX1_Buffer[i] = RX1_Buffer[i] - 'a' + 'A';//小写转大写
        }
        Flash_addr = GetAddress();
        if(Flash_addr < 0x80000000)
        {
            if(RX1_Buffer[0] == 'E')    //擦除
            {
                FlashSectorErase(Flash_addr);
                printf("已擦除一个扇区数据!\r\n");
                F0 = 1;
            }

            else if((RX1_Buffer[0] == 'W') && (RX1_Cnt >= 12) && (RX1_Buffer[10] == ' '))   //写入N个字节
            {
                j = RX1_Cnt - 11;
                for(i=0; i<j; i++)  BmmTxBuffer[i] = 0xff;      //检测要写入的空间是否为空
                SPI_Read_Nbytes(Flash_addr,j);
                i = SPI_Read_Compare(j);
                if(i > 0)
                {
                    printf("要写入的地址为非空,不能写入,请先擦除!\r\n");
                }
                else
                {
                    for(i=0; i<j; i++)  BmmTxBuffer[i] = RX1_Buffer[i+11];
                    SPI_Write_Nbytes(Flash_addr,j);     //写N个字节
                    SPI_Read_Nbytes(Flash_addr,j);
                    i = SPI_Read_Compare(j); //比较写入的数据
                    if(i == 0)
                    {
                        printf("已写入%bd字节数据!\r\n",j);
                    }
                    else        printf("写入错误!\r\n");
                }
                F0 = 1;
            }
            else if((RX1_Buffer[0] == 'R') && (RX1_Cnt >= 12) && (RX1_Buffer[10] == ' '))   //读出N个字节
            {
                j = GetDataLength();
                if((j > 0) && (j < EE_BUF_LENGTH))
                {
                    SPI_Read_Nbytes(Flash_addr,j);
                    printf("读出%bd个字节数据如下:\r\n",j);
                    for(i=0; i<j; i++)  UART1_TxByte(BmmRxBuffer[i]);
                    UART1_TxByte(0x0d);
                    UART1_TxByte(0x0a);
                    F0 = 1;
                }
            }
        }
    }
    if(!F0) printf("命令错误!\r\n");
}


//========================================================================
// 函数: void BMM_Config(void)
// 描述: SPI BMM 功能配置.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2021-5-6
//========================================================================
void BMM_Config(void)
{
        P_SW2 = 0x80;
        BMM_SPI_STA = 0x00;
        BMM_SPI_CFG = 0xE0;                //bit7 1:Enable Interrupt
        BMM_SPI_AMT = 0xff;                //设置传输总字节数:n+1

        BMM_SPI_TXA = BmmTxBuffer;
        BMM_SPI_RXA = BmmRxBuffer;
//        BMM_SPI_TXAH = (u8)(BMM_TX_ADDR >> 8);        //SPI发送数据存储地址
//        BMM_SPI_TXAL = (u8)BMM_TX_ADDR;
//        BMM_SPI_RXAH = (u8)(BMM_RX_ADDR >> 8);        //SPI接收数据存储地址
//        BMM_SPI_RXAL = (u8)BMM_RX_ADDR;

        BMM_SPI_CFG2 = 0x01;        //01:P2.2
        BMM_SPI_CR = 0x81;                //bit7 1:使能 SPI_BMM, bit6 1:开始 SPI_BMM 主机模式, bit0 1:清除 SPI_BMM FIFO
}

//========================================================================
// 函数: void UART1_TxByte(u8 dat)
// 描述: 发送一个字节.
// 参数: 无.
// 返回: 无.
// 版本: V1.0, 2014-6-30
//========================================================================
void UART1_TxByte(u8 dat)
{
    SBUF = dat;
    B_TX1_Busy = 1;
    while(B_TX1_Busy);
}

//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void SetTimer2Baudraye(u16 dat)  // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
    AUXR &= ~(1<<4);    //Timer stop
    AUXR &= ~(1<<3);    //Timer2 set As Timer
    AUXR |=  (1<<2);    //Timer2 set as 1T mode
    T2H = dat / 256;
    T2L = dat % 256;
    IE2  &= ~(1<<2);    //禁止中断
    AUXR |=  (1<<4);    //Timer run enable
}

//========================================================================
// 函数: void UART1_config(u8 brt)
// 描述: UART1初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART1_config(u8 brt)    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{
    /*********** 波特率使用定时器2 *****************/
    if(brt == 2)
    {
        AUXR |= 0x01;       //S1 BRT Use Timer2;
        SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate1);
    }

    /*********** 波特率使用定时器1 *****************/
    else
    {
        TR1 = 0;
        AUXR &= ~0x01;      //S1 BRT Use Timer1;
        AUXR |=  (1<<6);    //Timer1 set as 1T mode
        TMOD &= ~(1<<6);    //Timer1 set As Timer
        TMOD &= ~0x30;      //Timer1_16bitAutoReload;
        TH1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) / 256);
        TL1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) % 256);
        ET1 = 0;    //禁止中断
        INTCLKO &= ~0x02;  //不输出时钟
        TR1  = 1;
    }
    /*************************************************/

    SCON = (SCON & 0x3f) | 0x40;    //UART1模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
//  PS  = 1;    //高优先级中断
    ES  = 1;    //允许中断
    REN = 1;    //允许接收
    P_SW1 &= 0x3f;
    P_SW1 |= 0x00;      //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4

    B_TX1_Busy = 0;
    TX1_Cnt = 0;
    RX1_Cnt = 0;
}

//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: UART1中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART1_int (void) interrupt 4
{
    if(RI)
    {
        RI = 0;
        if(RX1_Cnt >= UART1_BUF_LENGTH) RX1_Cnt = 0;
        RX1_Buffer[RX1_Cnt] = SBUF;
        RX1_Cnt++;
        RX1_TimeOut = 5;
    }

    if(TI)
    {
        TI = 0;
        B_TX1_Busy = 0;
    }
}

/******************* FLASH相关程序 ************************/
#define SFC_WREN        0x06        //串行Flash命令集
#define SFC_WRDI        0x04
#define SFC_RDSR        0x05
#define SFC_WRSR        0x01
#define SFC_READ        0x03
#define SFC_FASTREAD    0x0B
#define SFC_RDID        0xAB
#define SFC_PAGEPROG    0x02
#define SFC_RDCR        0xA1
#define SFC_WRCR        0xF1
#define SFC_SECTORER1   0xD7        //PM25LV040 扇区擦除指令
#define SFC_SECTORER2   0x20        //W25Xxx 扇区擦除指令
#define SFC_BLOCKER     0xD8
#define SFC_CHIPER      0xC7

#define SPI_CE_High()   P_PM25LV040_CE  = 1     // set CE high
#define SPI_CE_Low()    P_PM25LV040_CE  = 0     // clear CE low
#define SPI_Hold()      P_SPI_Hold      = 0     // clear Hold pin
#define SPI_UnHold()    P_SPI_Hold      = 1     // set Hold pin
#define SPI_WP()        P_SPI_WP        = 0     // clear WP pin
#define SPI_UnWP()      P_SPI_WP        = 1     // set WP pin

/************************************************************************/
void SPI_init(void)
{
    SPCTL |=  (1 << 7); //忽略 SS 引脚功能,使用 MSTR 确定器件是主机还是从机
    SPCTL |=  (1 << 6); //使能 SPI 功能
    SPCTL &= ~(1 << 5); //先发送/接收数据的高位( MSB)
    SPCTL |=  (1 << 4); //设置主机模式
    SPCTL &= ~(1 << 3); //SCLK 空闲时为低电平,SCLK 的前时钟沿为上升沿,后时钟沿为下降沿
    SPCTL &= ~(1 << 2); //数据 SS 管脚为低电平驱动第一位数据并在 SCLK 的后时钟沿改变数据
    SPCTL = (SPCTL & ~3) | 3;   //SPI 时钟频率选择, 0: 4T, 1: 8T,  2: 16T,  3: 2T
    AUXR1 = (AUXR1 & ~(3<<2)) | (1<<2);     //IO口切换. 0: P1.2/P5.4 P1.3 P1.4 P1.5, 1: P2.2 P2.3 P2.4 P2.5, 2: P5.4 P4.0 P4.1 P4.3, 3: P3.5 P3.4 P3.3 P3.2

    P_PM25LV040_SCK = 0;    // set clock to low initial state
    P_PM25LV040_SI = 1;
    SPSTAT = SPIF + WCOL;   //清0 SPIF和WCOL标志
}

/************************************************************************/
void SPI_WriteByte(u8 out)
{
    SPDAT = out;
    while((SPSTAT & SPIF) == 0) ;
    SPSTAT = SPIF + WCOL;   //清0 SPIF和WCOL标志
}

/************************************************************************/
u8 SPI_ReadByte(void)
{
    SPDAT = 0xff;
    while((SPSTAT & SPIF) == 0) ;
    SPSTAT = SPIF + WCOL;   //清0 SPIF和WCOL标志
    return (SPDAT);
}

/************************************************
检测Flash是否准备就绪
入口参数: 无
出口参数:
    0 : 没有检测到正确的Flash
    1 : Flash准备就绪
************************************************/
void FlashCheckID(void)
{
    SPI_CE_Low();
    SPI_WriteByte(SFC_RDID);        //发送读取ID命令
    SPI_WriteByte(0x00);            //空读3个字节
    SPI_WriteByte(0x00);
    SPI_WriteByte(0x00);
    PM25LV040_ID1 = SPI_ReadByte();         //读取制造商ID1
    PM25LV040_ID  = SPI_ReadByte();         //读取设备ID
    PM25LV040_ID2 = SPI_ReadByte();         //读取制造商ID2
    SPI_CE_High();

//        printf("ID1=%bx\r\n",PM25LV040_ID1);
//        printf("ID=%bx\r\n",PM25LV040_ID);
//        printf("ID2=%bx\r\n",PM25LV040_ID2);
       
    if((PM25LV040_ID1 == 0x9d) && (PM25LV040_ID2 == 0x7f))  B_FlashOK = 1;  //检测是否为PM25LVxx系列的Flash
    else if(PM25LV040_ID == 0x12)  B_FlashOK = 2;                           //检测是否为W25X4x系列的Flash
    else if(PM25LV040_ID == 0x13)  B_FlashOK = 3;                           //检测是否为W25X8x系列的Flash
    else                                                    B_FlashOK = 0;
}

/************************************************
检测Flash的忙状态
入口参数: 无
出口参数:
    0 : Flash处于空闲状态
    1 : Flash处于忙状态
************************************************/
u8 CheckFlashBusy(void)
{
    u8  dat;

    SPI_CE_Low();
    SPI_WriteByte(SFC_RDSR);        //发送读取状态命令
    dat = SPI_ReadByte();           //读取状态
    SPI_CE_High();

    return (dat);                   //状态值的Bit0即为忙标志
}

/************************************************
使能Flash写命令
入口参数: 无
出口参数: 无
************************************************/
void FlashWriteEnable(void)
{
    while(CheckFlashBusy() > 0);    //Flash忙检测
    SPI_CE_Low();
    SPI_WriteByte(SFC_WREN);        //发送写使能命令
    SPI_CE_High();
}

/************************************************
擦除整片Flash
入口参数: 无
出口参数: 无
************************************************/
/*
void FlashChipErase(void)
{
    if(B_FlashOK)
    {
        FlashWriteEnable();             //使能Flash写命令
        SPI_CE_Low();
        SPI_WriteByte(SFC_CHIPER);      //发送片擦除命令
        SPI_CE_High();
    }
}
*/

/************************************************
擦除扇区, 一个扇区4KB
入口参数: 无
出口参数: 无
************************************************/
void FlashSectorErase(u32 addr)
{
    if(B_FlashOK)
    {
        FlashWriteEnable();             //使能Flash写命令
        SPI_CE_Low();
        if(B_FlashOK == 1)
        {
            SPI_WriteByte(SFC_SECTORER1);    //发送扇区擦除命令
        }
        else
        {
            SPI_WriteByte(SFC_SECTORER2);    //发送扇区擦除命令
        }
        SPI_WriteByte(((u8 *)&addr)[1]);           //设置起始地址
        SPI_WriteByte(((u8 *)&addr)[2]);
        SPI_WriteByte(((u8 *)&addr)[3]);
        SPI_CE_High();
    }
}

/************************************************
从Flash中读取数据
入口参数:
    addr   : 地址参数
    buffer : 缓冲从Flash中读取的数据
    size   : 数据块大小
出口参数:
    无
************************************************/
void SPI_Read_Nbytes(u32 addr, u16 size)
{
    if(size == 0)   return;
    if(!B_FlashOK)  return;
    while(BmmFlag);                     //BMM忙检测
    while(CheckFlashBusy() > 0);        //Flash忙检测

    SPI_CE_Low();                       //enable device
    SPI_WriteByte(SFC_READ);            //read command

    SPI_WriteByte(((u8 *)&addr)[1]);    //设置起始地址
    SPI_WriteByte(((u8 *)&addr)[2]);
    SPI_WriteByte(((u8 *)&addr)[3]);

    BmmFlag = 1;
    BMM_SPI_AMT = size-1;                //设置传输总字节数:n+1
    BMM_SPI_CR |= 0x40;     //开始SPI_BMM主模式操作
}

/************************************************************************
读出n个字节,跟指定的数据进行比较, 错误返回1,正确返回0
************************************************************************/
u8 SPI_Read_Compare(u16 size)
{
    u8  j;
    if(size == 0)   return 2;
    if(!B_FlashOK)  return 2;
    while(BmmFlag);                         //BMM忙检测

    do
    {
        if(BmmRxBuffer[j] != BmmTxBuffer[j])       //receive byte and store at buffer
        {
            return 1;
        }
        j++;
    }while(--size);         //read until no_bytes is reached
    return 0;
}


/************************************************
写数据到Flash中
入口参数:
    addr   : 地址参数
    size   : 数据块大小
出口参数: 无
************************************************/
void SPI_Write_Nbytes(u32 addr, u8 size)
{
    if(size == 0)   return;
    if(!B_FlashOK)  return;
    while(BmmFlag);                     //BMM忙检测
    while(CheckFlashBusy() > 0);        //Flash忙检测

    FlashWriteEnable();                 //使能Flash写命令

    SPI_CE_Low();                       // enable device
    SPI_WriteByte(SFC_PAGEPROG);        // 发送页编程命令
    SPI_WriteByte(((u8 *)&addr)[1]);    //设置起始地址
    SPI_WriteByte(((u8 *)&addr)[2]);
    SPI_WriteByte(((u8 *)&addr)[3]);

    BmmFlag = 1;
    BMM_SPI_AMT = size-1;                //设置传输总字节数:n+1
    BMM_SPI_CR |= 0x40;     //开始SPI_BMM主模式操作
}

//========================================================================
// 函数: void SPI_BMM_Interrupt (void) interrupt 49
// 描述: SPI BMM中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-5-8
// 备注:
//========================================================================
void SPI_BMM_Interrupt(void) interrupt 13
{
        BMM_SPI_STA = 0;
        BmmFlag = 0;
        SPI_CE_High();
}

使用特权

评论回复
xiaosun| | 2021-5-12 08:30 | 显示全部楼层
不知道单价要不要20元

使用特权

评论回复
资深技术| | 2021-5-13 16:09 | 显示全部楼层
楼上网友,QFP48 有没有?一样两片,替换8A8K试试。

使用特权

评论回复
风凌天| | 2022-4-27 09:49 | 显示全部楼层
你好,我想问两个问题:
1.那个PWM0引脚与PWM0_3引脚都可以同时输出PWM吗?还是只能用其中一个,类似的PWM1和PWM1_1这些
2.外部晶振应该接多大,或在什么范围内。

使用特权

评论回复
STCMCUNT018| | 2022-4-27 13:03 | 显示全部楼层
风凌天 发表于 2022-4-27 09:49
你好,我想问两个问题:
1.那个PWM0引脚与PWM0_3引脚都可以同时输出PWM吗?还是只能用其中一个,类似的PWM1 ...

1.那个PWM0引脚与PWM0_3引脚都可以同时输出PWM吗?还是只能用其中一个=====是分时切换,只能 用一个,但他还有,PWM7,PWM6,PWM5.PWM4.PWM3,PWM2,PWM1,PWM0

2, 外部晶振应该接多大,或在什么范围内=======STC8A8K64D4-45MHz-LQFP64/48/44, 你 外接晶振 可以 4M - 45MHz, 内部时钟,烧录时可以选择使用 内部 4M - 45MHz时钟,不需外部时钟,不需外部复位 STC8A8K64D4-45I-LQFP64/RMB4.5

使用特权

评论回复
风凌天| | 2022-4-27 15:08 | 显示全部楼层
STCMCUNT018 发表于 2022-4-27 13:03
1.那个PWM0引脚与PWM0_3引脚都可以同时输出PWM吗?还是只能用其中一个=====是分时切换,只能 用一个,但 ...

我现在有一个项目需要同时输出12路PWM,那么这个就用不了吗?还是将分时缩到很短的时间进行切换就可以用到12路,甚至16路PWM输出。这样做可行吗?

使用特权

评论回复
STCMCUNT018| | 2022-4-28 08:52 | 显示全部楼层
风凌天 发表于 2022-4-27 15:08
我现在有一个项目需要同时输出12路PWM,那么这个就用不了吗?还是将分时缩到很短的时间进行切换就可以用 ...

STC8A8K64D4 的PWM 是总共 12路 QQ截图20220428085157.png

使用特权

评论回复
STCMCUNT018| | 2022-4-28 08:53 | 显示全部楼层
STC8G2K64S4-36I-LQFP48, 有 45路 PWM

使用特权

评论回复
风凌天| | 2022-4-28 10:44 | 显示全部楼层
STCMCUNT018 发表于 2022-4-28 08:53
STC8G2K64S4-36I-LQFP48, 有 45路 PWM

非常感谢你的解答

使用特权

评论回复
风凌天| | 2022-5-6 14:39 | 显示全部楼层
STCMCUNT018 发表于 2022-4-28 08:52
STC8A8K64D4 的PWM 是总共 12路

那我使用STC8A8K64D4这个芯片,写程序的时候只需要添加#include <STC8.H>头文件就可以了吧

使用特权

评论回复
STCMCUNT018| | 2022-5-6 15:35 | 显示全部楼层
风凌天 发表于 2022-5-6 14:39
那我使用STC8A8K64D4这个芯片,写程序的时候只需要添加#include 头文件就可以了吧 ...

QQ截图20220506153445.jpg

使用特权

评论回复
风凌天| | 2022-5-14 10:43 | 显示全部楼层

我现在使用STC8A8K64D4这个芯片,IO口有的设置为准双向口,有的为推挽输出,但是给这些IO口输出0时,用万用表量了几个芯片引脚,电压居然有0.8V,这是什么问题。
我用STC15W的时候没有出现这种情况,基本上是0电压的。麻烦帮我解答一下,谢谢

使用特权

评论回复
STCMCUNT018| | 2022-5-14 11:15 | 显示全部楼层
风凌天 发表于 2022-5-14 10:43
我现在使用STC8A8K64D4这个芯片,IO口有的设置为准双向口,有的为推挽输出,但是给这些IO口输出0时,用万 ...

请贴上原理图,才好请人分析

使用特权

评论回复
风凌天| | 2022-5-14 11:53 | 显示全部楼层
STCMCUNT018 发表于 2022-5-14 11:15
请贴上原理图,才好请人分析

已设置为推挽输出,比如我现在在程序中给wx1=1;wx2=0;wx3=0,wx4=0,wx5=0;下完程序后5个数码管都会亮,应该只亮一个的;用万用表量了电压发现:wx1=5v,其它4个引脚均为0.8v

2.png
1.png

使用特权

评论回复
coody| | 2022-5-14 14:36 | 显示全部楼层
风凌天 发表于 2022-5-14 10:43
我现在使用STC8A8K64D4这个芯片,IO口有的设置为准双向口,有的为推挽输出,但是给这些IO口输出0时,用万 ...

只要设置了IO模式为推挽输出,则其操作与STC15系列是一样的。仔细检查IO设置,高电平驱动三极管,设置为准双向口驱动不了,才会出现你说的情况,只有0.6~0.8V电压,但是输出低电平依旧可以输出0V的。
另外,STC8A8K64D4有模拟电源AVCC和模拟地AGCD,AVCC一定要接与VCC一样的电压,AGDN要与GND一样的电位。

使用特权

评论回复
coody| | 2022-5-14 15:09 | 显示全部楼层
风凌天 发表于 2022-5-14 10:43
我现在使用STC8A8K64D4这个芯片,IO口有的设置为准双向口,有的为推挽输出,但是给这些IO口输出0时,用万 ...

另外,看你电路图,21脚GND这么串联了R51 1K电阻到GND?应该直接接到GND。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:---人活着就是要改变世界! ----51单片机,单片机中的拖拉机!

153

主题

2853

帖子

14

粉丝