CW32

1.利用NTC热敏电阻来采集温度具有高灵敏度、快速响应、宽温度测量范围和成本效益高等优势,这使得它成为许多应用中常用的温度传感器之一。

本期视频就来讲解基于CW32热敏电阻采集温度的应用。

资料链接如下:

BD网盘链接:

https://pan.baidu.com/s/1BBQ_7AmfirJPsXr2zxaOJw

提取码:6bc3

2.本实验用到了CW32 Cortex-M实训套件实验箱、NTC热敏电阻及Keil5开发环境。

1.png

CW32 Cortex-M实训套件

2.png

NTC热敏电阻实物

3.png

热敏电阻原理图

4.png

热敏电阻主要技术参数

5.png

6.png

7.png

3.NTC具体原理可观看视频,或参考以下链接中的文章

https://zhuanlan.zhihu.com/p/179181715

4.核心代码

/*

用TFT屏幕显示

1.热敏电阻的阻值

2.转换得到的温度

显示界面如下

-----------------------        
    NTC
  R:****   
  Tempture:****
-----------------------
*/

#include "main.h"
#include "table.h"   //阻温表
#include "config.h"
#include "LCD_calculate.h"
#include "Lcd_Driver.h"

char temp[10];  
unsigned char cnt = 0;
float dat_AD = 0;  //ADC采集电压
int R = 0;  //电阻值
float tempture = 0; //温度

void ADC_Configuration(void); // ADC初始化配置
float Get_ADC(void);  //采集电压值

int main()
{    
   unsigned char max,min,mid;    
   unsigned  int temp;  
   char strings[10];
  
  RCC_Configuration(); //时钟配置  A
  DC_Configuration(); //ADC配置
  
  Lcd_Init();          //TFT屏初始化配置  
  Lcd_Clear(GRAY0);    //清屏操作
  
  Gui_DrawFont_GBK16(0,0,WHITE,RED,"       NTC      "); //第一行:ADC TEST:  
  Gui_DrawFont_GBK16(0,32,WHITE,RED,"R:");             //第二行:R:  
  Gui_DrawFont_GBK16(0,48,WHITE,RED,"Temperature:"); //第三行:Temperature:
  
  while(1)  
  {        
     dat_AD = Get_ADC()*3.3/4095;  //单片机的参考电压为3.3V,12位的ADC    
     R = dat_AD*10000/(5-dat_AD);//VCC接5V,测得VCC实际电压值为4.58V,更为精准
    
    max = 97;  //温度上限97    
    min = 0;   //温度下限0
    
    while(1)   //二分查找法    
    {      
        mid = ( max + min ) / 2;      
        if( Table[mid] < R ) max = mid;      
        else min = mid;      
        if( (max-min) <= 1 ) break;    
    }
    
    if ( max == min ) tempture = min * 10;    
    else    
    {      
        temp = ( Table[min] - Table[max] ) / 10;  //计算温度的小数部分      
        temp = ( Table[min] - R ) / temp;           
        tempture = temp;       
        tempture = 10 * min + tempture;  //扩大十倍,方便计算    
    }    
    tempture /= 10;
    
    sprintf(strings,"%d",R);  //TFT显示阻值    
    Gui_DrawFont_GBK16(16,32,WHITE,RED,strings);    
    Gui_DrawFont_GBK16(48,32,WHITE,RED,"          ");
    
    sprintf(strings,"%.1f",tempture);    
    Gui_DrawFont_GBK16(96,48,WHITE,RED,strings); //TFT显示温度
    
    delay_ms(500);  
    }
}
float Get_ADC() //ADC采集
{  
   unsigned int temp_dat_adc = 0;
  ADC_SoftwareStartConvCmd(ENABLE);  
  while(ADC_GetITStatus(ADC_IT_EOC))  
  {    
      ADC_ClearITPendingBit(ADC_IT_EOC);          
      temp_dat_adc=ADC_GetConversionValue();        
  }  
  return   (float)temp_dat_adc;
}

5.实验最终现象

8.png

实验接线箱

9.jpg

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 20

前言 

CW32 电容式触摸按键设计指南向客户提供一种利用 CW32 内部资源结合软件编程实现电容式触摸按键有效 触摸检测的方法。本指南的内容重点在于工作原理、软件检测过程以及调试指引。 

利用芯源半导体的 CW32 系列小规模 MCU 的 IO、比较器、定时器、高速高精度内置 RC 时钟源以及高算力 等功能,通过检测电路端子电容的微小变化和波动,实现电容式触摸按键功能。其外围电路简单,占用资源 比例不高,非常有利于用户在节约 BOM 成本的前提下拓展功能。结合适当的工业化设计,触摸按键比接触 式按键更美观、耐磨的同时,还具有防水、抗干扰、寿命长等多种优势。 

通过本文,您会了解到如何利用内置电压比较器和内置定时器及软件配合,实现灵活方便的按键检测。本文 在介绍标准演示板(如下图)和演示软件的性能参数的同时,还会给出详细的调试建议以及设计参数选择倾 向分析,用以帮助客户快速而自信地完成设计并实施调试。

1.png

1、 电容触摸检测基本原理

独立于电路的金属部件都能够作为电容触摸传感器使用,其原理在于金属部件附近存在手指时,相当于增加 了金属部件对地的旁路电容。因此,利用 CW32 系列 MCU 的 IO 口对金属部件充电,并检测电容放电时间的 变化,理论上能够辨别金属部件附近是否存在手指按压动作。当无手指存在时,金属部件的电容为 Cp,其放 电时间为 t1;当存在手指时,增加的旁路电容为 Cx,此时的放电时间为 t2,如下图所示,可以看出两者之间 的放电时间是不一样的:

2.png

2、 基于 CW32F003 的触摸按键方案简介

由于 CW32F003 集成了电压比较器 VC 和定时器,因此触摸按键方案可以通过软件来实现,其实现的原来框 图如下所示:

3.png

其过程如下:

1. GTIM 配置为门控计数方式,计数源为芯片内部的 PCLK 时钟。 

2. VC 比较器的同相端配置为按键的接口,反相段配置为参考,参考的来源为芯片的 VCC 通过内部电阻网 络分压得到,VC 比较器输出极性不反转。

3. GPIO 口配置为数字输出,输出高电平对电容充电。由于电容容值比较小,充电电流较大(图中红色箭 头所示),电容上的电压很快达到 VCC。

4. GTIM的计数器 CNT清 0,GPIO口配置为输入高阻态,电容上的电荷基本通过 R泄放(图中蓝色箭头所示), 需要一定的时间,此时电容上的电压要比 VC 比较器的反相端的电压高,VC 输出高电平,是 GTIM 的门 控信号有效,GTIM 进行计数。 

5. 当电容上的电压降低到比 VC 比较器的反相端的参考电压低时,VC 输出低电平,GTIM 停止计数,同时 VC 比较将产生一个中断信号,此时读取 GTIM 的 CNT 的计数值,和判决门限比较可以判断是否发生触 摸按键的事件。如下图所示:

4.png

3、 电容触摸检测电路软件过程

在范例程序中,软件定时(用定时器中断实现)对每个被测 IO 充电并检测放电时间 N 次,N 次循环检测后, 将统计结果提交滤波器状态机,得到按键当前状态。每次检测的具体过程如下: 

1. 将 IO 口置高 2 个机器周期,此时金属部件及电容 C 对 GND 的电压被充高到 VCC。 

2. 将 IO 口配置为电压比较器输入模式,此时 IO 口状态切换为高阻输入状态,金属部件及电容 C 通过对 GND 的旁路电阻 R 放电,端子电压变化曲线为标准的 RC 放电曲线。 

3. 软件记录循环定时器(GTIM 最高主频运行)的当前值,并等待电压比较器的输出翻转(电压比较器被 配置为与某电压门限比较)。

4. 电压比较器输出翻转后立即记录循环定时器当前值,并结合前次记录的时间记录输出结果。触摸检测过程的相关代码如下:

uint32_t TouchKey_GetValue(uint8_t key, uint8_t ref) 
{ 
    uint32_t CurTime; 
    //VC1  切换通道 
    CW_VC1->CR0_f.INP = key;       // 设置按键通道 
    CW_VC1->DIV_f.DIV = ref;     // 设置按键比较的参考比例 
    // 获取放电时间 
    CW_GPIOB->DIR &= ~((1UL<<8)>>key);   // 按键端口输出,对电容充电 
    __NOP(); 
    __NOP(); 
    CW_GTIM->CNT = 0x0000;        // 计数器清零 
    CW_GPIOB->DIR |= ((1UL<<8)>>key);   // 按键端口输入高阻 
    while((CW_VC1->SR_f.FLTV) == 1 );     // 等到放电到比较点 
    CurTime = CW_GTIM->CNT;        // 获取放电时间 
    return CurTime; 
}

4、 触摸参数及选型倾向 

为了保证检测流程顺利执行,需要选择每一个触摸按键的基础电容 C 和放电电阻 R 以及比较器参考门限 V。DEMO 中,这三个参数一般为 C=4.7pF,R=51KΩ,V=9/64 VDD。

C 和 R 的值,以及比较器参考门限 V 均可根据实际电路测试结果进行调整,调整考量如下: 

1. C 的容量增加会令放电时间更长,在检测程序中将会需要更多的机器周期等待比较器翻转。

2. C 的容量增加会显著增强电路稳定性但对检测灵敏度没有大的影响。 

3. R 的阻值增加会令放电时间更长,在检测程序中将会需要更多的机器周期等待比较器翻转。 

4. R 的阻值增加会降低电路稳定性(高阻易受环境干扰)但对检测灵敏度有明显帮助。 

5. 比较器参考门限 V 过高会降低检测灵敏度,但能节约检测时间。门限 V 过低会削弱抗干扰能力并浪费检 测时间。

5、 调试指引及性能参考 

5.1 示例软件框架介绍 

示例软件占用 1 个基本定时器,利用定时中断并在中断服务程序中执行按键检测过程、定时周期 10 毫秒。每次进入中断服务程序后,顺序扫描 M 个触摸按键的 RC 响应。 

顺序扫描 N 次后,将 RC 响应结果数据提交滤波器状态机。

滤波器状态机输出按键状态结果。

5.2 调试工具 TD_GetBaseResponseRCT 的使用 

示例软件提供一组标定工具来测量当前环境的 RC 响应,执行过程如下:

1. 在没有手指按下的情况下,执行 TD_GetBaseResponseRCT,函数的参数用于选择对应 IO,返回值作 为该按键的基础时长 TB。 

2. 在有手指按下的情况下,执行 TD_GetBaseResponseRCT,函数的参数用于选择对应 IO,返回值作为 该按键的信号时长 TS。 

注 1:每一个按键(IO)的 TB和 TS都应被单独收集并作为滤波器状态机的参数使用。 

注 2:各种温湿度条件下的 TB和 TS都应该在实验室中被采集并用于影响滤波器状态机的参数。 

注 3:比较器门限 V 也是可以针对每一个触摸按键单独选择的,如果某个按键的 TB和 TS无法实现明显的差异, 调节 C、R 和 V 将是唯一有效的途径。 

另:由于本例利用了高阻态 及小信号检测技术,触摸按键的布线要求尽量保持独立性,其金属部件、与 IO 的连线以及 RC 电路周围要尽量避免与其它电路并列共存,否则将大幅提高参数选择及调试难度直至无法完成。

5.3 性能参考

5.png

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 14

模数转换器(ADC)的主要功能是将模拟量转换为数字量,方便MCU进行处理。下面以CW32L083为例介绍CW系列的模数转换器的特点和功能,并提供演示实例。

一、概述

CW32L083 内部集成一个 12 位精度、最高 1M SPS 转换速度的逐次逼近型模数转换器 (SAR ADC),最多可将 16 路模拟信号转换为数字信号。现实世界中的绝大多数信号都是模拟量,如光、电、声、图像信号等,都要由 ADC 转换成数字信号,才能由 MCU 进行数字化处理。

二、主要特性

• 12 位精度 

• 可编程转换速度,最高达 1M SPS 

• 16 路输入转换通道:13 路外部引脚输入 - 内置温度传感器 - 内置 BGR 1.2V 基准 - 1/3 VDDA 电源电压 

• 4 路参考电压源(Vref):- VDDA 电源电压 - ExRef(PB00)引脚电压 - 内置 1.5V 参考电压 - 内置 2.5V 参考电压 

• 采样电压输入范围:0 ~ Vref

 多种转换模式,全部支持转换累加功能 - 单次转换 - 多次转换 - 连续转换 - 序列扫描转换 - 序列断续转换 

• 支持单通道、序列通道两种通道选择,最大同时支持 8 个序列 

• 支持输入通道电压阈值监测

• 内置信号跟随器,可转换高阻抗输入信号 

• 支持片内外设自动触发 ADC 转换 

• 支持 ADC 转换完成触发 DMA

三、转换时序

ADC 的转换时序如下图所示:

1.png


向 ADC 控制寄存器 ADC_CR0 的 EN 位域写入 1,使能 ADC 模块。 

ADC_CR0.EN 由 0 变为 1 约 40μs 后 ADC_ISR.READY 标志位置 1,表示模拟电路初始化完成,可以开始进行 ADC 转换。 

向 ADC 启动寄存器 ADC_START 的 START 位域写入 1,启动 ADC 转换,转换完成后硬件自动清零。 

ADC 工作时钟 ADCCLK,由系统时钟 PCLK 经预分频器分频得到,通过控制寄存器 ADC_CR0 的 CLK 位域可选择 1 ~ 128 分频


四、工作模式

ADC 控制寄存器 ADC_CR0 的 MODE 位域配置 ADC 工作模式


启动 ADC 转换,可通过向 ADC 启动寄存器 ADC_START 的 START 位域写 1;也可通过其他外设来触发。

2.png

五、实际案例

GTIM1定时器定时1S,定时器1S中断触发启动ADC转换,采样AIN1,并通过GTIM2以PWM方波输出ADC采样值:PWM占空比50%,周期为1Hz-5000Hz,对应ADC的0-4095采样值。

1.配置ADC测试IO口

void ADC_PortInit(void) 
{ 
    REGBITS_SET(CW_SYSCTRL->AHBEN, SYSCTRL_AHBEN_GPIOA_Msk); //打开GPIO时钟 
    REGBITS_SET(CW_SYSCTRL->APBEN2, SYSCTRL_APBEN2_ADC_Msk); //打开ADC时钟 
    PA01_ANALOG_ENABLE();//set PA01 as AIN1 INPUT 
}

2.LED初始化

void LED_Init(void) 
{ 
    GPIO_InitTypeDef GPIO_InitStructure = {0}; 
    REGBITS_SET(CW_SYSCTRL->AHBEN, SYSCTRL_AHBEN_GPIOC_Msk); //打开GPIO时钟 
    /* Configure the GPIO_LED pin */ 
    GPIO_InitStructure.Pins = GPIO_PIN_2 | GPIO_PIN_3; 
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 
    GPIO_Init(CW_GPIOC, &GPIO_InitStructure); 
    PC02_SETLOW();//LEDs are off. PC03_SETLOW(); 
}

3.PWM IO初始化

void PWM_PortInit(void) 
{ 
    GPIO_InitTypeDef GPIO_InitStructure = {0}; 
    /* PA5 PWM 输出 */ 
    __RCC_GPIOA_CLK_ENABLE(); 
    /* Configure the PWM output pin */ 
    GPIO_InitStructure.Pins = GPIO_PIN_5; 
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure); 
    PA05_AFx_GTIM2CH1(); 
}

4.GTIM初始化

void GTIM_Init(void) 
{ 
    GTIM_InitTypeDef GTIM_InitStruct = {0}; 
    
    //REGBITS_SET(CW_SYSCTRL->APBEN1, SYSCTRL_APBEN1_GTIM1_Msk); //打开GTIM1 
    __RCC_GTIM1_CLK_ENABLE(); //打开GTIM1时钟 GTIM_InitStruct.Mode = GTIM_MODE_TIME;
    GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE; 
    GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV1024; 
    GTIM_InitStruct.ReloadValue = 62499ul; //T=1s. 
    GTIM_InitStruct.ToggleOutState = DISABLE; 
    GTIM_TimeBaseInit(CW_GTIM1, &GTIM_InitStruct); 
    GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE); 
    NVIC_ClearPendingIRQ(GTIM1_IRQn); 
    NVIC_EnableIRQ(GTIM1_IRQn);
    NVIC_SetPriority(GTIM1_IRQn, 0x03); 
    
    __RCC_GTIM2_CLK_ENABLE();//打开GTIM2时钟 
    GTIM_InitStruct.ReloadValue = 0xFFFFu; 
    GTIM_InitStruct.ToggleOutState = ENABLE;
    GTIM_TimeBaseInit(CW_GTIM2, &GTIM_InitStruct); 
    valuePeriod = GTIM_InitStruct.ReloadValue; 
    valuePosWidth = valuePeriod >> 1u; 
    GTIM_OCInit(CW_GTIM2, GTIM_CHANNEL1, GTIM_OC_OUTPUT_PWM_HIGH); 
    GTIM_SetCompare1(CW_GTIM2, valuePosWidth); 
    GTIM_Cmd(CW_GTIM2, ENABLE); 
}

5.主程序main

uint16_t valueAdc; 
uint32_t valueAdcAcc; 
volatile uint8_t gFlagIrq; 
uint16_t gCntEoc = 0; 
uint8_t cntSample; 
float fTsDegree; 
uint32_t valuePeriod; 
uint32_t valuePosWidth; 
uint32_t valueReload = 0xFFFFu; 
int main(void) 
{   
    uint8_t res; 
    ADC_InitTypeDef ADC_InitStructure = {0}; 
    ADC_WdtTypeDef ADC_WdtStructure = {0};
    ADC_SingleChTypeDef ADC_SingleChStructure = {0}; 
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);     //以下从HSI切换到PLL 
    RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000UL, RCC_PLL_MUL_8); 
    //开启PLL,PLL源为HSI 
    __RCC_FLASH_CLK_ENABLE();//打开FLASH时钟 
    FLASH_SetLatency(FLASH_Latency_3); 
    res = RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL); //切换系统时钟到PLL:64MHz。 
    ADC_PortInit();//配置ADC测试IO口 
    LED_Init();//LED初始化 
    PWM_PortInit();
    GTIM_Init(); 
    ADC_StructInit(&ADC_InitStructure); //ADC默认值初始化 
    ADC_WdtInit(&ADC_WdtStructure); //ADC模拟看门狗通道初始化 
    ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div128;    //ADCCLK:500KHz. 
    ADC_InitStructure.ADC_InBufEn = ADC_BufEnable; 
    ADC_InitStructure.ADC_SampleTime = ADC_SampTime10Clk; 
    ADC_SingleChStructure.ADC_DiscardEn = ADC_DiscardNull; //配置单通道转换模式
    ADC_SingleChStructure.ADC_Chmux = ADC_ExInputCH1; //选择ADC转换通道 
    ADC_SingleChStructure.ADC_InitStruct = ADC_InitStructure; 
    ADC_SingleChStructure.ADC_WdtStruct = ADC_WdtStructure; 
    ADC_SingleChOneModeCfg(&ADC_SingleChStructure); 
    ADC_ITConfig(ADC_IT_EOC, ENABLE); 
    ADC_EnableIrq(ADC_INT_PRIORITY);
    ADC_ClearITPendingAll(); 
    ADC_Enable();//ADC使能 
    ADC_ExtTrigCfg(ADC_TRIG_GTIM1, ENABLE); //ADC外部中断触发源配置 
    GTIM_Cmd(CW_GTIM1, ENABLE);
    while (1) 
    { 
        while (!(gFlagIrq & ADC_ISR_EOC_Msk)); 
        gFlagIrq = 0u; PC03_TOG(); 
        valueAdc = ADC_GetConversionValue(); 
        valueReload = ((4095u * 125000ul) / (4999u * valueAdc + 4095u) + 1) >> 1; 
        GTIM_SetCounterValue(CW_GTIM2, 0u);     //reset. 
        GTIM_SetReloadValue(CW_GTIM2, valueReload); 
        GTIM_SetCompare1(CW_GTIM2, valuePosWidth); //等待ADC外部中断触发源启动下一次ADC转换 
    } 
}

6.实验展示

通用定时器GTIM1定时1s自动触发ADC模块进行转换,ADC通道为AIN1:PA01。

通用定时器GTIM2将AIN1的ADC采样值转换成频率可变的PWM方波,占空比50%,使用PA05作为PWM输出。ADC采样值为0时,PWM方波频率为1Hz;ADC采样值为4095时,PWM方波频率为5KHz。

3.png

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 31

别家MCU的工作电压范围常见的是2.4V~3.6V或者1.8V~5.5V。咱们这个CW32系列设计的是1.65V~5.5V是怎么考虑的?

兼容5V电平是目前国产MCU的工作电压范围的发展趋势,让客户可以随意选择合适的工作电压,以方便与外围器件接口。这也主要得益于华虹宏力相关工艺制程能够提供支持这个电压范围的Pad Stack。

但是与别家不同的是,CW32设计团队拥有独立自主的设计能力和完备的质量管理体系,有能力提供更宽的工作电压范围。CW32系列MCU,特别是CW32L这样的低功耗MCU,采用1.65V这样的设计,可以实现与一些1.8V电压系统的直接兼容连接.

毕竟芯片设计可以保证1.65V供电正常工作,实际测试验证电压更是低至1.60V,这样的工作环境下,别家1.8V~5.5V的芯片设计在仅供电1.8V的时候用到了工作电压下限了,在遇到干扰的时候怕是不稳定。

除了低电压下限问题以外,还有什么好处或者缺点呢?

如此宽的工作电压范围,也是同时为实现超强抗干扰性能服务的。CW32系列MCU能够在工作中自动适应工作电压的动态变化,还可以显著延长电池供电系统的电池使用寿命。

如果一定要说有什么不好,也不妨说出来,兼容5伏的Pad Stack一般信号输入输出的速度不会如3.3伏或者更低的逻辑电平那样快,满足十几二十兆的速度就是够用的了,毕竟在工控和低功耗的场合,这样的使用场景不会很广的,基本都够用。

有些MCU芯片是支持3.3伏供电但是容忍5伏输入的,CW32有这个功能吗?

CW32系列没有这个功能,一般建议客户如果要连接5伏信号给MCU,就将MCU的电源供电直接给到5伏,这样就不需要有这个功能了。另外,就算是别家MCU具备这种5伏容忍功能的情况下,别家MCU的供应商也是不推荐这么用的,毕竟信号电平不同,还需要处理电源上电顺序,不是一个安全简易的设计风格。

所以说,支持1.65V的最低工作电压的MCU,CW32是唯一的一家吗?

技术都是在不断进步的,目前CW32能够确保这一技术要点。客户可能不一定用得到,也许客户的供电电压就是一个可以保证的3.3V或者5V。

但是我们不要忘了,业界最宽的工作电压范围,表征的是CW32的设计团队是一个不断进取的独立设计团队,在确保安全的前提下致力于为客户提供更可靠的技术和产品。这本身就说明CW32系列拥有高远的技术目标,不会满足于现有水平,在各个技术领域都敢于破局、甘当先锋。

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 63

CW32的LVD低电压检测器适用于监测VDDA电源电压或外部引脚输入电压,当被监测电压与LVD阈值的比较结果满足触发的条件时,LVD将会产生中断或者复位信号,通常用来处理一些紧急任务。LVD产生的中断或复位标志,只能通过软件程序清零,并且只有当中断或复位标志被清零后,在再次达到触发条件时,LVD才能再次产生中断或复位信号。在本文中以CW32L083系列为例,介绍LVD的基本功能和使用例程。

LVD的基本功能介绍:

1、4路监测电压源

VDDA电源电压,PA00引脚输入,PB00引脚输入,PB11引脚输入

2、16阶阈值电压,范围2.02V-3.76V

3、3种触发条件,可以组合使用

电平触发:电压低于阈值

下降沿触发:电压跌落到阈值以下的下降沿

上升沿触发:电压回升到阈值以上的上升沿

4、可触发产生中断或复位信号,二者不能同时产生

5、8阶滤波可配置

6、支持迟滞功能

7、支持低功耗模式下运行,中断唤醒MCU

1.png

通过LVD的控制寄存器LVD_CR0的SOURCE位域来选择LVD模块监控的电压(VDDA电源/ PA00引脚/PB00引脚/PB11引脚),在监测外部引脚电压时,需将对应的GPIO端口配置为模拟输入模式(GPIOx_ANALOG.PINy = 1)。

LVD的比较结果可以从PA01/PA08/PC12/PE02/PF02脚输出,在此之前,需将对应的GPIO口配置为数字输出模式,同时选择端口位LVDOUT复用功能。

LVD 内置的电压比较器具有迟滞功能,只有当被监测电压高于或低于阈值电压达到 20mV 时,比较器输出信号才会发生翻转,可避免当 LVD 的监测电压在阈值电压附近时,电压比较器的输出结果发生频繁翻转,增强系统抗干扰能力。具体波形如下图所示:

2.png

LVD的阈值电压根据LVD控制寄存器LVD_CR0的VTH位控制。

3.png

LVD支持数字滤波功能,可以增强系统的鲁棒性(系统在一定的参数抖动下,维持起某些性能的特性),可以将LVD电压比较的输出结果信号进行数字滤波,小于滤波宽度的信号被滤除,不会被触发中断或复位,如下图所示,图中两处噪音或其他信号就被滤除了。

4.png

通过设置控制寄存器LVD_CR1的FLTEN位域,可以使能数字滤波模块,当将该位设置为1的时候,会使能数字滤波模块。

通过设置控制寄存器 LVD_CR1 的 FLTCLK 位域可以选择数字滤波的时钟:

• FLTCLK 位为 1,选择 HSIOSC 作为滤波时钟 

• FLTCLK 位为 0,选择内置 RC 振荡器时钟作为滤波时钟,其频率约 150kHz

控制寄存器 LVD_CR1 的 FLTTIME 位域用于选择数字滤波的时钟个数,如下表所示:

5.png

从 LVD 状态寄存器 LVD_SR 的 FLTV 位域,可以读出经 LVD 数字滤波后的信号电平;当 GPIO 的功能复用为 LVD_OUT 时,数字滤波后的信号就可以从 GPIO 输出,以方便观察测量。

LVD 支持在低功耗模式下工作,中断输出可将芯片从低功耗模式下唤醒。当被监测电压与 LVD 阈值的比较结果满足触发条件时,可产生中断或复位信号。产生中断还是复位信号由控制寄存器 LVD_CR0 的 ACTION 位域控制:

 • ACTION 为 1,LVD 触发产生复位 #define LVD_Action_Reset  ((uint32_t)0x00000002)

 • ACTION 为 0,LVD 触发产生中断 #define LVD_Action_Irq   ((uint32_t)0x00000000)

LVD可以通过设置控制寄存器 LVD_CR0 的 IE 位域为 1,使能 LVD 中断,满足触发条件时将产生 LVD 中断,中断标志位 LVD_SR.INTF 会被硬件置 1,用户可以向 INTF 位写 0,清除中断标志。设置控制寄存器 LVD_CR1 的 LEVEL、FALL、RISE 位域,可选择不同的中断或复位触发方式,三者可组合使用:

• LEVEL 为 1,被监测电压低于阈值时触发中断或产生复位 

• FALL 为 1,被监测电压跌落到阈值以下的下降沿触发中断或产生复位 

• RISE 为 1,被监测电压回升到阈值以上的上升沿触发中断或产生复位

LVD使用例程介绍:

根据上述内容,可以配置一个关于CW32L083的电压监测例程,LVD的输入通道设置为PA00,输出端口为PA08,门限电压为2.02V,利用LVD的中断实现当LVD输入通道电压低于或者高于门限电压时刻(利用上升沿和下降沿),PC03输出电平翻转一次。

void LVD_PortInit(void) 
{ 
    GPIO_InitTypeDef GPIO_InitStructure = {0}; 
    
    //打开GPIOA时钟 
    __RCC_GPIOA_CLK_ENABLE(); 
    
    //将PA08设置为LVD比较结果输出 
    GPIO_InitStructure.Pins = GPIO_PIN_8; 
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure); 
    
    //将PA08复用为LVD比较结果输出     
    PA08_AFx_LVDOUT();
     //将PA00设置为LVD的输入口 
    PA00_ANALOG_ENABLE(); 
} 

int main(void) 
{ 
    LVD_InitTypeDef LVD_InitStruct = {0}; 
    
    //LED初始化 
    LED_Init(); 
    
    //配置测试IO口 
    LVD_PortInit(); 
    
    LVD_InitStruct.LVD_Action = LVD_Action_Irq;  //配置中断功能 
    LVD_InitStruct.LVD_Source = LVD_Source_PA00; //配置LVD输入口为PA00 
    LVD_InitStruct.LVD_Threshold = LVD_Threshold_2p02V; //配置LVD基准电压为2.02v 
    LVD_InitStruct.LVD_FilterEn = LVD_Filter_Enable;//LVD滤波模块开启 
    LVD_InitStruct.LVD_FilterClk = LVD_FilterClk_RC150K;//LVD滤波时钟为150KHz 
    LVD_InitStruct.LVD_FilterTime = LVD_FilterTime_4095Clk; 
    LVD_Init(&LVD_InitStruct);
    
    LVD_TrigConfig(LVD_TRIG_FALL | LVD_TRIG_RISE, ENABLE);//LVD中断为上升沿和下降沿触发 
    LVD_EnableIrq(LVD_INT_PRIORITY); 
    LVD_ClearIrq(); 
    FirmwareDelay(4800); 
    LVD_Enable(); //LVD使能 
    
    while (1) 
    { 
        if (gFlagIrq) 
        { 
            PC03_TOG(); 
            gFlagIrq = FALSE; 
        } 
    } 
} 

/** 
* @brief LED I/O初始化 
* 
*/ 
void LED_Init(void) 
{ 
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    
    //打开GPIOC时钟 
    REGBITS_SET(CW_SYSCTRL->AHBEN, SYSCTRL_AHBEN_GPIOC_Msk); 
    
    /* Configure the GPIO_LED pin */ 
    GPIO_InitStructure.Pins = GPIO_PIN_2 | GPIO_PIN_3; 
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 
    GPIO_Init(CW_GPIOC, &GPIO_InitStructure); 
    
    //LEDs are off. 
    PC02_SETLOW(); 
    PC03_SETLOW(); 
} 

//LVD中断服务函数 
void LVD_IRQHandler(void) 
{ 
    LVD_ClearIrq();      //清除中断标志 
    gFlagIrq = TRUE;     //将gFlagIrq赋值为TURE 
}

根据上述例程可以得到在PA00的输入电压值低于2.02v或高于2.02v的瞬间时刻,LVD会产生中断,PC03的输出电平会产生翻转,可利用CW32L083的开发板和一根杜邦线,将PA00和DVCC连接,在连接上的时刻以及拔掉杜邦线的时刻,LED1的状态会发生翻转。

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 164

一、波特率介绍

波特率表示单位时间内传送的码元符号的个数,它是对符号传输速率的一种度量,它用单位时间内载波调制状态改变的次数来表示,即指一个单位时间内传输符号的个数(Baud,单位符号:Bd)。

CW32L083 内部集成 6 个通用异步收发器 (UART),支持异步全双工、同步半双工和单线半双工模式,支持硬件数据流控和多机通信;可编程数据帧结构,可以通过小数波特率发生器提供宽范围的波特率选择。UART 控制器工作在双时钟域下,允许在深度休眠模式下进行数据的接收,接收完成中断可以唤醒 MCU 回到运行模式。

波特率发生器框图如下:

1.png

二、波特率设置

1.同步半双工模式下

波特率计算公式:BaudRate = UCLK / 12

其中,UCLK 是 UART 的传输时钟,其来源可以是 PCLK、LSE 或 LSI,通过控制寄存器 UARTx_CR2 的 SOURCE 位域来选择。

2.异步模式下

UART 的接收和发送波特率是相同的,由同一个波特率发生器产生。波特率发生器支持 16 倍采样、8 倍采样、4 倍采样和专用采样这 4 种采样模式,具体的采样模式通过控制寄存器 UARTx_CR1 的 OVER 位域来选择。UCLK是 UART的传输时钟,其来源可以是 PCLK、LSE或 LSI,具体来源通过 UARTx_CR2.SOURCE来选择。BRRI(UARTx_BRRI[15:0]),是波特率计数器的整数部分,可设置范围为 1 ~ 65535。BRRF(UARTx_BRRF[3:0]),是波特率计数器的小数部分,可设置范围为 0 ~ 15。

OVER = 00,设置 16 倍采样,波特率计算公式:BaudRate = UCLK / ( 16×BRRI + BRRF )

OVER = 01,设置 8 倍采样,波特率计算公式:BaudRate = UCLK / ( 8×BRRI )

OVER = 10,设置 4 倍采样,波特率计算公式:BaudRate = UCLK / ( 4×BRRI )

OVER = 11,设置专用采样,波特率计算公式:BaudRate = ( 256×UCLK ) / BRRI

专用采样仅适合传输时钟源为 LSE 或者 LSI 时,进行 2400bps、4800bps 或 9600bps 波特率下的 UART 通信。

UCLK 为 24MHz 波特率设置示例(OVER = 00)

2.png

UCLK 为 32.768kHz 波特率设置示例(OVER = 11)

3.png

3.波特率自动检测 

CW32L083 使用 UART 作为从机进行通信时,可以通过自动波特率检测的方法,自动适应 UART 主机的波特率。可将通用定时器(GTIM)的输入捕获来源配置为 UART 的 RXD 信号,或者将 GTIM 的门控信号配置为 UART 的 RXD 信号,配合使用相关软件算法测量 UART 的波特率,以实现波特率自适应。

三、波特率计数器寄存器定义

1.UARTx_BRRI 波特率计数器整数部分寄存器

Address offset: 0x0C  Reset value: 0x0000 0000

4.png

2.UARTx_BRRF 波特率计数器小数部分寄存器

Address offset: 0x10  Reset value: 0x0000 0000

5.png

四、波特率设置举例

当传输时钟 UCLK 的频率为 24MHz 时,要求配置 BaudRate = 115200 bps,计算 16×BRRI + BRRF = 24000000 / 115200 = 208.33 则: 

BRRI = 208.33 / 16 = 13.02,最接近的整数是:13(0x0D) 

BRRF = 0.02×16 = 0.32,最接近的整数是:0(0x00) 

即需要设置 UARTx_BRRI 为 0x0D,UARTx_BRRF 为 0x00 此时,实际波特率 BaudRate = 115384.62 bps,误差率为 0.16%。

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 29

4月25-28日,2023年中国国际表计行业年度大会顺利举办。在这次展会中,武汉芯源半导体携CW32家族产品,为表计行业参观者展示了燃气表、水表、电表、可燃气体报警器等表计产品应用方案,以及料位开关、温控器、无刷电机等工业类应用方案,吸引了专业用户、外国友人、主办方媒体的驻足咨询了解。

1.png

2.png

3.png

4.png

同时,武汉芯源半导体还受邀出席了“2023年数智四表创新技术和产品开放日大会”,技术总监张总进行了主题为“造芯新势力-CW32系列MCU”的精彩分享,从智能表计市场对MCU的需求、CW32在智能表计市场的应用、CW32在智能表计市场的优势等方面进行了介绍。

5.jpg

技术总监张总表示,相比其他行业,表计行业需要MCU长时间持续稳定运行,通常要求十年以上。所以,智能表计市场对MCU的可靠性和稳定性有很高的要求,需要MCU具有高等级的ESD和EFT等可靠性指标,可以确保产品整个生命周期稳定工作。而CW32已公开HBM ESD 8KV等测试结果,全部ESD和Latch Up指标达到国际标准最高等级。

6.png

此外,智能表计市场对MCU有着极其苛刻的超低功耗要求,需要nA级超低待机功耗、uS级高速唤醒、uA/MHz级高能效比。CW32L031低功耗系列产品支持Sleep和DeepSleep两种低功耗工作模式,在最低功耗模式下工作电流仅为450nA,从DeepSleep模式下唤醒时间仅为4us,能够满足表计市场的需求。

7.png

最后,智能表计市场对MCU的生态环境和服务支持也是有要求的,需要完备的软硬件工具链以及技术支持。CW32提供完善的研发资料,配套工具,支持主流开发环境,配备专业技术交流论坛等,也可以很好的支持表计应用。

8.png

9.png

10.png

具体到表计市场的应用,技术总监张总介绍了CW32在智能水表、智能燃气表以及智能电表上的应用。

11.png

12.png

13.png

这也体现了低功耗CW32L系列已得到部分智能表计市场的认可。同时,武汉芯源半导体也将紧跟市场的需求,深入调研用户需求,将给客户提供更丰富的产品选择,欢迎更多行业伙伴前来洽谈合作。

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 15

通用异步收发器(Universal Asynchronous Receiver/Transmitter:UART),是一种通用串行数据总线,常用于系统内各子模块间的数据交换。

以CW32L083为例,CW32L083 内部集成 6 个通用异步收发器 (UART),支持异步全双工、同步半双工和单线半双工模式,支持硬件数据流控和多机通信;可编程数据帧结构,可以通过小数波特率发生器提供宽范围的波特率选择。UART 控制器工作在双时钟域下,允许在深度休眠模式下进行数据的接收,接收完成中断可以唤醒 MCU 回到运行模式。

一、主要功能

• 支持双时钟域驱动:配置时钟 PCLK;传输时钟 UCLK。

• 可编程数据帧结构:数据字长:8、9 位,LSB 在前;校验位:无校验、奇校验、偶校验;停止位长度:1、1.5、2 位 。

• 16 位整数、4 位小数波特率发生器 。

• 支持异步全双工、同步半双工、单线半双工 。

• 支持硬件流控 RTS、CTS。

• 支持直接内存访问 (DMA) 。

• 支持多机通信,自动地址识别 。

• 6 个带中断标志的中断源 。

• 错误检测:奇偶校验错误、帧结构错误 。

• 低功耗模式下收发数据,中断唤醒 MCU。

1.功能框图

UART 控制器挂载到 APB 总线上,配置时钟域 PCLK,固定为 APB 总线时钟 PCLK,用于寄存器配置逻辑工作;传输时钟域 UCLK,用于数据收发逻辑工作,其来源可选择 PCLK 时钟、外部低速时钟(LSE)以及内部低速时钟 (LSI)。双时钟域的设计更便于波特率的设置,支持从深度休眠模式下唤醒控制器。

1.png

2.UART中断

UART 控制器支持 6 个中断源,当 UART 中断触发事件发生时,中断标志位会被硬件置位,如果设置了对应的中断使能控制位,将产生中断请求。CW32L083 的一个 UART 模块使用一个系统 UART 中断,UART 中断是否产生中断跳转由嵌套向量中断控 制器 (NVIC) 的中断使能设置寄存器 NVIC_ISER 的相应位控制。系统 UART 中断示意图如下图所示:

2.png

在用户 UART 中断服务程序中,应查询相关 UART 中断标志位,以进行相应的处理,在退出中断服务程序之前, 要清除该中断标志位,避免重复进入中断程序。各 UART 中断源的标志位、中断使能位、中断标志清除位或清除方法,如下表所示:

3.png

3.CH340介绍

CH340是一个USB总线的转接芯片,实现USB协议和UART协议的自动转换。 

4.png

RTS#:MODEM联络输出信号,请求发送

UD+:直接连接USB总线的D+数据线

UD-:直接连接USB总线的D-数据线

V3:在3.3V电源电压时链接VCC输入外部电源,在5V电源电压时外接容量为100nF的退耦电容。

VCC:正电源输入端,需要接100nF电源退耦电容

TXD:串行电路输出

RXD:串行数据输入,内置可控上拉和下拉电阻

CH340内置了独立的收发缓冲区,支持单工、半双工或者全双工异步串行通讯。串行数据包括1个低电平起始位、5、6、7或8个数据位、1个或2个高电平停止位,支持奇校验/偶校验/标志校验/空白校验。CH340支持常用通讯波特率:50、75、100、110、134.5、150、300、600、900、1200、1800、2400、3600、4800、9600、14400、19200、28800、33600、38400、56000、57600、76800、115200、128000、153600、230400、460800、921600、1500000、2000000等。串口发送信号的波特率误差小于0.3%,串口接收信号的允许波特率误差不小于2%。

二、实例演示

本实例采用CW32L083V8T6的StartKit单板,MCU的串口引脚(PA08/ PA09)和CH340对接,CH340通过USB接口和PC机对接,实现PC机软件和MCU通过UART双向通信功能。

单板启动后,处于等待数据接收状态,当有数据接收到后,产生UART接收中断,在中断中读取接收到的数据,然后将数据通过UART再发送回来,并清除中断标志位,然后等待接收下一个数据。

1.配置RCC系统时钟

void RCC_Configuration(void)
{
    //SYSCLK = HSI = 8MHz = HCLK = PCLK
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);
    //外设时钟使能
    RCC_AHBPeriphClk_Enable(DEBUG_UART_GPIO_CLK, ENABLE);
    DEBUG_UART_APBClkENx(DEBUG_UART_CLK, ENABLE);
}

2.GPIO配置

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    //UART TX RX 复用
    DEBUG_UART_AFTX;
    DEBUG_UART_AFRX;
    GPIO_InitStructure.Pins = DEBUG_UART_TX_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(DEBUG_UART_TX_GPIO_PORT, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = DEBUG_UART_RX_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(DEBUG_UART_RX_GPIO_PORT, &GPIO_InitStructure);
}

3.UART配置

void UART_Configuration(void)
{
    UART_InitTypeDef UART_InitStructure = {0};
    UART_InitStructure.UART_BaudRate = DEBUG_UART_BaudRate;
    UART_InitStructure.UART_Over = UART_Over_16;
    UART_InitStructure.UART_Source = UART_Source_PCLK;
    UART_InitStructure.UART_UclkFreq = DEBUG_UART_UclkFreq;
    UART_InitStructure.UART_StartBit = UART_StartBit_FE;
    UART_InitStructure.UART_StopBits = UART_StopBits_1;
    UART_InitStructure.UART_Parity = UART_Parity_No ;
    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
    UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
    UART_Init(DEBUG_UARTx, &UART_InitStructure);
}

4.配置NVIC

void NVIC_Configuration(void)
{
    //优先级,无优先级分组
    NVIC_SetPriority(DEBUG_UART_IRQ, 0);
    //UARTx中断使能
    NVIC_EnableIRQ(DEBUG_UART_IRQ);
}

5.中断函数处理UART2/UART5

void UART2_UART5_IRQHandler(void)
{
     /* USER CODE BEGIN */
    uint8_t TxRxBuffer;
    if(UART_GetITStatus(CW_UART5 UART_IT_RC) != RESET)// 获取UARTx中断标志位
    {
        TxRxBuffer = UART_ReceiveData_8bit(CW_UART5;// 通过UARTx接收一个数据(8bit)
        UART_SendData_8bit(CW_UART5 TxRxBuffer);// 通过UARTx发送一个数据(8bit)
        UART_ClearITPendingBit(CW_UART5 UART_IT_RC);// 清除UARTx中断标志位
    }
    /* USER CODE END */
}

6.定义常量define

//UARTx
#define  DEBUG_UARTx                   CW_UART5
#define  DEBUG_UART_CLK                RCC_APB1_PERIPH_UART5
#define  DEBUG_UART_APBClkENx          RCC_APBPeriphClk_Enable1
#define  DEBUG_UART_BaudRate           9600
#define  DEBUG_UART_UclkFreq           8000000
//UARTx GPIO
#define  DEBUG_UART_GPIO_CLK           RCC_AHB_PERIPH_GPIOB
#define  DEBUG_UART_TX_GPIO_PORT       CW_GPIOB
#define  DEBUG_UART_TX_GPIO_PIN        GPIO_PIN_8
#define  DEBUG_UART_RX_GPIO_PORT       CW_GPIOB
#define  DEBUG_UART_RX_GPIO_PIN        GPIO_PIN_9
//GPIO AF
#define  DEBUG_UART_AFTX               PB08_AFx_UART5TXD()
#define  DEBUG_UART_AFRX               PB09_AFx_UART5RXD()
//中断
#define  DEBUG_UART_IRQ                UART2_UART5_IRQn

7.UART中断方式接收数据

int32_t main(void)
{
     //配置RCC
    RCC_Configuration();
    //配置GPIO
    GPIO_Configuration();
    //配置UART
    UART_Configuration();
    //配置NVIC
     NVIC_Configuration();
    //使能UARTx RC中断
     UART_ITConfig(DEBUG_UARTx, UART_IT_RC, ENABLE);
    UART_SendString(DEBUG_UARTx, "\r\nCW32L083 UART Interrupt\r\n");
    while(1)
    {
        //中断收发
    }
}

8、测试结果如下:当MCU收到上位机发送的数据后,再回传到上位机,UART功能正常。

5.png

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 90

一、概述

CW32L083 内部集成 2 个 I2C 控制器,能按照设定的传输速率(标准,快速,高速)将需要发送的数据按照 I2C 规范串行发送到 I2C 总线上,或从总线上接收数据,并对通信过程中的状态进行检测,另外还支持多主机通信中的总线冲突和仲裁处理。

二、主要功能

• 支持主机发送 / 接收,从机发送 / 接收四种工作模式 

• 支持时钟延展 ( 时钟同步 ) 和多主机通信冲突仲裁

• 支持标准 (100Kbps)/ 快速 (400Kbps)/ 高速 (1Mbps) 三种工作速率 

• 支持 7bit 寻址功能 

• 支持 3个从机地址 

• 支持广播地址 

• 支持输入信号噪声过滤功能 

• 支持中断状态查询功能

1.协议介绍

I2C 总线使用两根信号线(数据线 SDA 和时钟线 SCL)在设备间传输数据。SCL 为单向时钟线,固定由主机驱动。SDA 为双向数据线,在数据传输过程中由收发两端分时驱动。I2C 总线上可以连接多个设备,所有设备在没有进行数据传输时都处于空闲状态(未寻址从机接收模式),任一设备都可以作为主机发送 START 起始信号来开始数据传输,在 STOP 停止信号出现在总线上之前,总线一直处于 被占用状态。I2C 通信采用主从结构,并由主机发起和结束通信。主机通过发送 START 起始信号来发起通信,之后发送 SLA+W/R 共 8bit 数据(其中,SLA 为 7bit 从机地址,W/R 为读写位),并在第 9个SCL 时钟释放 SDA 总线, 对应的从机在第 9个SCL 时钟占用 SDA 总线并输出 ACK 应答信号,完成从机寻址。此后根据主机发送的第 1 字 节的 W/R 位来决定数据通信的发端和收端,发端每发送 1个字节数据,收端必须回应 1个ACK 应答信号。数据传输完成后,主机发送 STOP 信号结束本次通信。

2.功能框图

I2C 模块主要包括时钟发生器、输入滤波器、地址比较器、协议控制逻辑、仲裁和同步逻辑、以及相关寄存器等。

1.png

CW32L083 支持用户灵活选择 GPIO 作为 I2C 通信引脚,如下表所示:

2.png

3.I2C中断

I2C 控制寄存器 I2Cx_CR 的 SI 位域为中断标志位。当 I2C 状态寄存器 I2Cx_STAT 的 STAT 位域值发生改变(变成 0xF8 除外)时,I2Cx_CR.SI 标志位就会被置位,同时产生中断请求。在用户 I2C 中断服务程序中,应查询 I2C 状态寄存器 I2Cx_STAT 的 STAT 位域值获取 I2C 总线的状态,以确定中断产生原因。设置 I2Cx_CR.SI 为 0 清除该标志位。

4.工作模式

I2C 控制器支持 4 种工作模式:主机发送模式、主机接收模式、从机发送模式、从机接收模式。另外还支持广播 接收模式,其工作方式和从机接收模式类似。

三、EEPROM(CW24C02AD)

1.功能简介

CW24C02是一个2Kbit的串行EEPROM存储芯片,可存储256个字节数据。芯片内部分为32页,每页8字节。工作电压范围为1.7V到5.5V,I2C接口时钟频率为 1MHz (5V,3V),400 KHz (1.7V)。器件地址为1010 A2 A1 A0,对于我们单板A2 A1 A0引脚全部接GND,故器件地址为1010000,即0x50。器件内部存储空间地址长度8 bit。

2.读写时序

字节写操作时序:起始信号+器件地址(7bit)+读写指示(1bit)+存储空间地址(8bit)+写入数据(8bit)+停止信号,即可完成指定字节写入操作。

3.png

页写操作时序:起始信号+器件地址(7bit)+读写指示(1bit)+存储空间地址(8bit)+写入数据(8bit*8)+停止信号,即可完成指定地址(必须是页起始地址)的页写入操作。

4.png

随机读操作时序:起始信号+器件地址(7bit)+读写指示(1bit)+存储空间地址(8bit)+重复起始信号+器件地址(7bit)+读写指示(1bit),之后器件会返回1字节数据,主机收到后发送停止信号,即可完成指定字节读取操作。

5.png

顺序读操作时序:和随机读时序类似,只是在主机接收到1字节数据后,不发送停止信号,而是继续发送时钟进行下一个字节数据的接收,直到所有所需读取的数据全部读取,之后再发送停止信号。

四、硬件连接

如下图所示,MCU和EEPROM通过I2C总线互连。

6.png

五、实例演示:MCU采用页写和顺序读操作时序完成EERPOM的访问。

1.I2C读写EEPROM芯片中断函数(I2C分为I2C1和I2C2)

void I2c1EepromReadWriteInterruptFunction(void)
{
    u8State = I2C_GetState(CW_I2C1);// I2C:获取状态寄存器函数
    switch(u8State)
    {
        case 0x08:     //发送完START信号
        I2C_GenerateSTART(CW_I2C1, DISABLE);// 发送START信号
        I2C_Send7bitAddress(CW_I2C1, I2C_SLAVEADDRESS,0X00);// 做主时发送从机地址字节
        break;
        case 0x10:     //发送完重复起始信号
        I2C_GenerateSTART(CW_I2C1, DISABLE);
        if(0 == SendFlg)
        {
            I2C_Send7bitAddress(CW_I2C1, I2C_SLAVEADDRESS,0X00);    //写命令
        }
        else
        {
            I2C_Send7bitAddress(CW_I2C1, I2C_SLAVEADDRESS,0X01); //读命令,eeprom 随机读
        }
        break;
        case 0x18:    //发送完SLA+W/R字节
        I2C_GenerateSTART(CW_I2C1, DISABLE);
        I2C_SendData(CW_I2C1, u8Addr);   //发送访问EEPROM的目标地址字节
        break;
        case 0x20:    //发送完SLA+W后从机返回NACK
        case 0x38:    //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲裁  或者  主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
        case 0x30:    //发送完一个数据字节后从机返回NACK
        case 0x48:    //发送完SLA+R后从机返回NACK
        I2C_GenerateSTOP(CW_I2C1, ENABLE);
        I2C_GenerateSTART(CW_I2C1, ENABLE);
        break;
        case 0x58:    //接收到一个数据字节,且NACK已回复
        u8Recdata[u8RecvLen++] = I2C_ReceiveData(CW_I2C1);//所有数据读取完成,NACK已发送
        receivedflag =1;
        I2C_GenerateSTOP(CW_I2C1, ENABLE);//发送停止条件
        break;
        case 0x28:     //发送完1字节数据:发送EEPROM中memory地址也会产生,发送后面的数据也会产生
        if(0 == SendFlg)
        {
            if(u8SendLen <WRITELEN)
            {
                 I2C_SendData(CW_I2C1,u8Senddata[u8SendLen++]);
            }
            else
            {
                u8SendLen = 0;
                Comm_flg = 1;
                SendFlg = 1;
                I2C_GenerateSTOP(CW_I2C1, ENABLE);//发送完数据,发送停止信号
            }
        }
        else//SendFlg=1为读,SendFlg=0为写。读数据发送完地址字节后,重复起始条件
        {
            CW_I2C1->CR_f.STA = 1;  //set start       //发送重复START信号,START生成函数改写后,会导致0X10状态被略过,故此处不调用函数
            I2C_GenerateSTOP(CW_I2C1, DISABLE);
        }
        break; 
        case 0x40:     //发送完SLA+R信号,开始接收数据
        u8RecvLen = 0;
        if(READLEN>1)
        {
            I2C_AcknowledgeConfig(CW_I2C1,ENABLE);//读取数据超过1个字节才发送ACK
        }
        break;
        case 0x50:     //接收完一字节数据,在接收最后1字节数据之前设置AA=0;
        u8Recdata[u8RecvLen++] = I2C_ReceiveData(CW_I2C1);
        if(u8RecvLen==READLEN-1)
        {
            I2C_AcknowledgeConfig(CW_I2C1,DISABLE);;
        }
        break;
    }
    I2C_ClearIrq(CW_I2C1);
}

2.设置系统时钟

void RCC_Configuration(void)
{
    CW_SYSCTRL->APBEN1_f.I2C1 = 1U;   
}

3.设置GPIO口

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    CW_SYSCTRL->AHBEN_f.GPIOA  = 1;
    CW_SYSCTRL->AHBEN_f.GPIOB  = 1;
    CW_SYSCTRL->AHBEN_f.GPIOC  = 1;
    CW_SYSCTRL->AHBEN_f.GPIOD  = 1;
    CW_SYSCTRL->AHBEN_f.GPIOE  = 1;
    CW_SYSCTRL->AHBEN_f.GPIOF  = 1;
    
    PB10_AFx_I2C1SCL();
    PB11_AFx_I2C1SDA();
    GPIO_InitStructure.Pins = I2C1_SCL_GPIO_PIN | I2C1_SDA_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_Init(I2C1_SCL_GPIO_PORT, &GPIO_InitStructure);
}

4.配置嵌套矢量中断控制器

void NVIC_Configuration(void)
{
     __disable_irq();
    NVIC_EnableIRQ(I2C1_IRQn);
    __enable_irq();
}
void I2C1_IRQHandler(void)
{
    I2c1EepromReadWriteInterruptFunction();
}

5.定义常量

#define  I2C1_SCL_GPIO_PORT       CW_GPIOB
#define  I2C1_SCL_GPIO_PIN        GPIO_PIN_10   
#define  I2C1_SDA_GPIO_PORT       CW_GPIOB
#define  I2C1_SDA_GPIO_PIN        GPIO_PIN_11    
//EEPROM内部地址
uint8_t u8Addr = 0x00;        //地址字节
#define WRITELEN   8          //写数据长度
#define READLEN   8           //读数据长度
#define WriteReadCycle  35    //写读次数,每次写入数据为n+i(n为次数,i=0~7)
uint8_t u8Senddata[8] = {0x66,0x02,0x03,0x04,0x05,0x60,0x70,0x20};
uint8_t u8Senddata2[8] = {0x55,0xAA,0xAA,0x55,0x55,0xAA,0x55,0xAA};
uint8_t u8Recdata[16]= {0x00};
uint8_t u8SendLen=0;
uint8_t u8RecvLen=0;
uint8_t SendFlg = 0,Comm_flg = 0;
uint8_t u8recvflg=0;
uint8_t u8State = 0;
uint8_t receivedflag = 0;    //读取完成标志

6.主程序:利用I2C接口,采用中断方式读写EEPROM芯片(CW24C02)。

int32_t main(void)
{
    I2C_InitTypeDef I2C_InitStruct = {0};
    uint16_t tempcnt = 0 ;
    RCC_Configuration();//时钟初始化
    GPIO_Configuration();//IO口初始化
    //I2C初始化
    I2C_InitStruct.I2C_Baud = 0x01;//500K=(8000000/(8*(1+1)) ,波特率计数器配置
    I2C_InitStruct.I2C_BaudEn = ENABLE;// 波特率计数器使能
    I2C_InitStruct.I2C_FLT = DISABLE; //<FLT配置
    I2C_InitStruct.I2C_AA =  DISABLE; //<ACK配置
    I2C1_DeInit();
    I2C_Master_Init(CW_I2C1,&I2C_InitStruct);//初始化模块
    NVIC_Configuration();//中断设置
    //I2C模块开始工作
    I2C_Cmd(CW_I2C1,ENABLE);  //模块使能
    tempcnt =0;
    for(uint8_t i=0; i<8; i++)
    {
        u8Senddata[i] = i;
    }
    while(1)
    {
        I2C_GenerateSTART(CW_I2C1, ENABLE); //开始信号
        while(1)
        {
            while(!Comm_flg) ; //等待数据发送完成
            FirmwareDelay(3000);
            
            Comm_flg = 0; //启动读数据过程
            receivedflag=0;
            I2C_GenerateSTART(CW_I2C1, ENABLE); //开始信号
            while(!receivedflag) ; //等待数据读取完成
            receivedflag = 0; //初始化下一次写数据
            SendFlg = 0;
            u8RecvLen = 0;
            tempcnt++;
            for(uint8_t i=0; i<8; i++)
            {
                u8Senddata[i] =tempcnt+i;
            }
            break;
        }

        if(tempcnt >=WriteReadCycle) //测试次数完成,退出
        {
            break;
        }
    }
    while(1);
}

7.程序流程

程序完成I2C主设备配置后,先将u8Senddata数组中的内容写入到EEPROM的第1页(CW24C02每页8字节):发送START信号后,I2C模块会产生状态改变中断,在中断服务程序中根据不同状态值进行不同处理,直到完成CW24C02的页写模式所有数据字节以及STOP信号发送,发送完成后置写操作流程完成标志。主循环中判断到写操作流程完成后,启动从EERROM的第1页数据读取流程:发送启动信号后,I2C模块会产生状态改变中断,在中断服务程序中根据不同状态值进行不同处理,直到完成CW24C02的顺序读模式所有数据字节发送及读取,在发送完STOP信号后置读操作流程完成标志。主循环中判断读操作流程完成后,初始化u8Senddata数组内容,重复下一次测试过程。完成WriteReadCycle变量设置的测试次数后退出。

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 31

页面

订阅 RSS - CW32