提示:本内容仅供学习,切勿商用!

上节课,咱们已经简单学习了IS器件与BAT32A单片机之间I2C通信!本文主要介绍中微BAT32A237系列单片机配置外部晶振方法,接着讲IS器件控制LED的流水、颜色编程!

本人只是一个普通的嵌入式低级(初级)玩家,文章主要针对新手,所以大佬要是发现文中有何不妥之处,还请口下留情,若能指出,我一定虚心改正。


前言

在这里插入图片描述

理论知识:一次讲透I2C总线时序图,单片机IIC通信协议底层【洋桃电子大百科P28】
理论知识还是很重要的,如若忘记了,请回顾看一下!

学习单片机,要对单片机电路设计浅浅了解一下,需要学习一下此视频:
1.构成单片机的最基本电路(知识点:RAM存储器 寄存器 时钟源 频率发生器 逻辑门电路 计算机基础 CD4069 CD4011)【极速入门数模电路P17】
2.设计的有多巧妙?一个器件把模拟波形转化为方波!施密特触发器的工作原理?


一、时钟发生电路分析【重点】

请下载此芯片的用户手册:中微半导-BAT32A237用户手册
先不要着急看下图,先去看以下资料,掌握相关知识点:
1.【BAT32G113-时钟发生电路控制LED时长-A5-2025/3/17】
2.【硬件干货】电脑的心脏——时钟神马情况?石英晶体为啥能够振起来
3.【单片机的心脏】时钟振荡部分

在这里插入图片描述
根据上图的时钟发生电路框图,你看过之后,可能有一点点蒙,不如,你先看看此篇文章:
1.三种常见单片机时钟电路方案,对比其优缺点
2.I2C-时钟频率与数据传输速率的理解

通过以上资料看完之后,你已经知道了:晶体振荡器产生的正弦波通过单片机处理后,变为了方波信号!方波信号频率为时钟发生电路的fmx(根据图上所知),如果说我买了一个12Mhz晶体振荡器,那么FMX、FIH、FUSB、FMAIN、FCLK是什么?其时钟频率的值由哪些寄存器决定(不用担心接下来首先回答这个问题)。不过,你最关心的问题是:“根据已知时钟发生电路框图的12Mhz晶体振荡器,如何通过代码配置将高速内部振荡器换成高速外部系统时钟振荡器所产生的时钟频率呢?还有就是配置I2C的时钟频率该如何操作呢?最后,配置的I2C时钟频率的计算方法是什么,才能达到I2C传输速率?
看完时钟发生电路框图之后,你发现有很多单词,如下图:(进行详细阐述)

1、FMX、FIH、FUSB、FMAIN、FCLK简介

在这里插入图片描述

图下内容概括:
1.高速内部振荡器就是单片机内部的晶振电路!默认是12Mhz时钟频率!
2.外部晶振就是我采用的无源晶振12Mhz,主要用于精度高的应用场景!
3.副系统时钟一般用于时钟!

在这里插入图片描述

2、FCLK的时钟来源有几种?

在这里插入图片描述

3、时钟发生电路关键寄存器简介

在这里插入图片描述

4、FCLK配置方法

在这里插入图片描述

这里我主要采用的是将内部晶振改为外部晶振,具体操作请根据上面描述和给的Demo自行研究!不过,在这之前你先接着往下看!

5、你最关心的问题

你提到这么多问题是不错的,不过得了解晶振知识和原理,链接如下:
1.晶振的工作原理
2.有源晶振和无源晶振的区别,别再说你分不清了!!!
3.皮尔斯振荡(581)
4.方波居然由正弦波组成!一句话告诉你它们的区别
这是皮尔斯振荡电路,如下图:

在这里插入图片描述

这里用示波器测得晶体振荡器的输出引脚发出正弦波的时钟频率约为12Mhz,

在这里插入图片描述

这是晶振另一端的类似12Mhz方波:(看起来是非常有点…,哈哈哈)

在这里插入图片描述

温馨提示:
1.上面图片我采用的是12Mhz无源晶振!有源晶振的波形跟无源晶振的波形是有区别的。
2.无源晶振是没有极性的,所以可以大胆随便接!不会影响到输入还是输出引脚,这个是由单片机内部硬件设定好的!

这里我先给出内部时钟频率切换外部时钟频率的Demo:

clk.c文件:

#include "clk.h"

/***********************************************************************************************************************
* Function Name: CLK_Osc_Setting
* @brief  This function initializes the Main OSC and Sub OSC.
* @param  main
*             - OSC_PORT:        X1, X2 as PORT
*             - OSC_OSCILLATOR:  X1, X2 as oscillator and connect crystal/ceramic resonator
*             - OSC_EXCLK:       X1, as PORT, X2 as external clock input
* @param  sub 
*             - OSC_PORT:        XT1, XT2 as PORT
*             - OSC_OSCILLATOR:  XT1, XT2 as oscillator and connect crystal resonator
*             - OSC_EXCLK:       XT1 as PORT, XT2 as external clock input
* @return None
***********************************************************************************************************************/
void CLK_Osc_Setting(osc_pin_mode_t main, osc_pin_mode_t sub)
{
    volatile uint32_t w_count;
    uint8_t           temp_stab_set;
    uint8_t           temp_stab_wait;
    uint8_t           tmp;

    tmp = 0x00;
    if(main == OSC_PORT )
    {
        tmp |= (0 << CGC_CMC_EXCLK_Pos) | (0 << CGC_CMC_OSCSEL_Pos);
    }

    if(sub == OSC_PORT )
    {
        tmp |= (0 << CGC_CMC_EXCLKS_Pos) | (0 << CGC_CMC_OSCSELS_Pos);
    }

    if(main == OSC_OSCILLATOR)
    {
        tmp |= (0 << CGC_CMC_EXCLK_Pos) | (1 << CGC_CMC_OSCSEL_Pos) | (1 << CGC_CMC_AMPH_Pos);
    }

    if(sub == OSC_OSCILLATOR)
    {
        tmp |= (0 << CGC_CMC_EXCLKS_Pos) | (1 << CGC_CMC_OSCSELS_Pos) | (1 << CGC_CMC_AMPHS_Pos);
    }

    if(main == OSC_EXCLK)
    {
        tmp |= (1 << CGC_CMC_EXCLK_Pos) | (1 << CGC_CMC_OSCSEL_Pos);
    }

    if(sub == OSC_EXCLK)
    {
        tmp |= (1 << CGC_CMC_EXCLKS_Pos) | (1 << CGC_CMC_OSCSELS_Pos);
    }

    CGC->CMC = tmp;

    /* Set fMX */
    CGC->CSC &= ~(1<<7) ;   //MSTOP = 0

    if(main == OSC_OSCILLATOR)
    {
        temp_stab_set = _FF_CGC_OSCSTAB_STA18;
        //temp_stab_set = _00_CGC_OSCSTAB_STA0;			     
        
        do
        {
            temp_stab_wait = CGC->OSTC;
            temp_stab_wait &= temp_stab_set;
        }
        while (temp_stab_wait != temp_stab_set);
    }

    /* Set fSUB */
    CGC->CSC &= ~(1<<6) ;   //XTSTOP = 0

    if(sub == OSC_OSCILLATOR)
    {
        /* Change the waiting time according to the system */
        for (w_count = 0U; w_count <= CGC_SUBWAITTIME; w_count++)
        {
            __NOP();
        }
    }
}

/***********************************************************************************************************************
* Function Name: CLK_Fclk_Select
* @brief  This function selects the system clock(fCLK). 
* @param  cks
*             - MAINCLK_FIH:     fIH as system clock(fCLK)
*             - MAINCLK_FMX:     fMX as system clock(fCLK)
*             - MAINCLK_FSUB:    fSUB as system clock(fCLK)
* @return CKC register value
***********************************************************************************************************************/
uint8_t CLK_Fclk_Select(clock_select_t cks)
{
    volatile uint32_t w_count;
    uint8_t           tmp;

    if(cks == MAINCLK_FIH) 
    {
        CGC->CKC = (0 << CGC_CKC_CSS_Pos) | (0 << CGC_CKC_MCM0_Pos );
        //CGC->CSC|= CGC_CSC_MSTOP_Msk;
    }

    if(cks == MAINCLK_FMX) 
    {
        CGC->CKC = (0 << CGC_CKC_CSS_Pos) | (1 << CGC_CKC_MCM0_Pos );
        //CGC->CSC|= CGC_CSC_HIOSTOP_Msk;

    }

    if(cks == MAINCLK_FSUB) 
    {
        CGC->CKC = (1 << CGC_CKC_CSS_Pos) | (0 << CGC_CKC_MCM0_Pos );
    }
    
    tmp = CGC->CKC;
    
    return tmp;
}

clk.h文件:

#include "BAT32A237.h"

/* 定义防止递归包含 ----------------------------------------------------------*/
#ifndef _clk_H
#define _clk_H

/* 宏定义 --------------------------------------------------------------------*/
#define _FF_CGC_OSCSTAB_STA18                      (0xFFU) /* 2^18/fX */
#define CGC_SUBWAITTIME               (360U)   /* change the waiting time according to the system */

typedef enum
{
    OSC_PORT,
    OSC_OSCILLATOR,
    OSC_EXCLK 
} osc_pin_mode_t;

typedef enum
{
    MAINCLK_FIH,
    MAINCLK_FMX,
    MAINCLK_FSUB 
} clock_select_t;


/* 函数申明 ------------------------------------------------------------------*/
void CLK_Osc_Setting(osc_pin_mode_t main, osc_pin_mode_t sub);
uint8_t CLK_Fclk_Select(clock_select_t cks);


#endif /* clk_H */

main.c文件:

#include "BAT32A237.h"
#include "clk.h"


int main()
{
	/* P121 P122外部晶振初始化 --------------------------------------------------------------------*/	
	CLK_Osc_Setting(OSC_OSCILLATOR, OSC_PORT);//对主振荡器(Main OSC)和副振荡器(Sub OSC)进行初始化操作
	
	CLK_Fclk_Select(MAINCLK_FMX);//选择主振荡器的时钟(fMX)作为系统时钟
	while((CGC->CKC & CGC_CKC_MCS_Msk) == 0);//进入一个循环,等待系统时钟切换完成	

	while(1)
	{
	}
}

在这里插入图片描述

上图,就是完整从内部时钟频率切换到外部时钟频率!

二、硬件I2C电路框图

目前我们知道了晶振的时钟频率为12Mhz,现在需要知道的是:I2C的时钟频率代码配置和测得SCL频率的关系!
下面是:硬件I2C的电路简易框图(大概了解一下寄存器的名称和箭头方向即可)

在这里插入图片描述
这里,得补充一下I2C总线有多种通信模式知识:

1.标准模式(Standard-mode):SCL时钟频率最高为 100kHz。
2.快速模式(Fast-mode):SCL时钟频率最高为 400kHz。
3.快速模式 Plus(Fast-mode Plus):SCL时钟频率范围为 1MHz到 3.4MHz。
4.高速模式(High-speed mode):SCL 时钟频率最高可达 3.4MHz。
温馨提示:如若你用示波器抓取I2C的SCL引脚的时钟频率,会发现频率值与配置的时钟频率不是一个数据!比如,我配置的是400Khz时钟频率,但我测得时钟频率为133Khz~200Khz。也就是100<SCL<400,得到时钟频率属于快速模式!

前面提到用了外部时钟12Mhz,FCLK的值为12Mhz。现在,需要知道SCL的时钟频率为多少?
计算思路如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里说明一下:这个是大概的思路,具体的算法思路是需要根据官方手册进行计算的!

二、代码分析和整改

根据官方的Demo文件,进行合适的应用层设计和修改,修改过程请参考此文章:【BAT32A237-使用硬件I2C接口实现控制IS32FL3265A功率器件-A1-2025/3/26】
温馨提示:IICA0_MasterSend函数的作用是采用硬件中断来多个发送字节的,通过标志位进行判断的!需要加入如下代码,来完成多个字节发送完成!此外,在初始化,请拿逻辑分析仪和示波器来抓取上电之后的时序波形,如若,出现时序问题,解决方法:在外部晶振初始化后加入阻塞延时一点时间,来稳定I2C通讯正常!

while(g_iica0_tx_end == 0);//多个字节发送完成标志

这是官方的Demo板:

在这里插入图片描述

谢谢观看

博主分享不易,请给一键三连哦(关注 + 点赞 + 收藏),你的鼓励是博主分享的动力。
彩蛋:我种草了一个公众号:嵌入式点灯大师,内容实在且丰富,有兴趣的可点击查看或关注!
下一章,将会继续讲解CAN通讯发送指令来控制单片机的状态!也就是CAN控制单片机,单片机通过I2C来控制IS器件,从而实现LED实现流水及颜色变化!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐