CW32

别家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)。

围观 71

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)。

围观 207

一、波特率介绍

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

围观 35

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)。

围观 17

通用异步收发器(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)。

围观 98

一、概述

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)。

围观 34

串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信。CW32L083 内部集成 2 个串行外设 SPI 接口,支持双向全双工、单线半双工和单工通信模式,可配置 MCU 作为 主机或从机,支持多主机通信模式,支持直接内存访问(DMA)。

一、主要功能

• 支持主机模式、从机模式 

• 支持全双工、单线半双工、单工 

• 可选的 4 位到 16 位数据帧宽度 

• 支持收发数据 LSB 或 MSB 在前 

• 可编程时钟极性和时钟相位

• 主机模式下通信速率高达 PCLK/2 

• 从机模式下通信速率高达 PCLK/4 

• 支持多机通信模式 

• 8 个带标志位的中断源 

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

1.功能框图

1.png

SPI 一般通过 4 个引脚与外部设备相连:

• MOSI 主机输出 / 从机输入,用于主机模式下的数据发送和从机模式下的数据接收; 

• MISO 主机输入 / 从机输出,用于主机模式下的数据接收和从机模式下的数据发送; 

• SCK 同步串行时钟,主机时钟输出和从机时钟输入,发送或接收主机同步时钟; 

• CS 从机选择,也用于多机通信时总线冲突检测

CW32L083 支持用户灵活选择 GPIO 作为 SPI 通信引脚,通过 AFR 功能复用实现,具体 SPI 引脚请参考数据手册中的引脚定义。

2.全双工模式

SPI 支持全双工通信模式,在该模式下,主机和从机的移位寄存器通过 MOSI、MISO 两条单向数据线进行连接, 在主机提供的 SCK 时钟信号的控制下同时进行两个方向的数据传输。设置控制寄存器 SPIx_CR1 的 MODE 位域为 0x0,使 SPI 工作于全双工通信模式。

2.png

二、FLASH存储器及电路连接

3.png

W25Q64JVSSIQ为华邦NOR型FLASH,存储容量为8Mbytes,每页256bytes,总共有32768个可编程页,最大一次可编程大小为256bytes。一次擦除大小可以为4K、32K、64K字节(K=1024)或者全擦除。支持标准SPI、Dual SPI、Quad SPI,最大操作时钟为133MHz。

芯片SPI接口时序要求如下:

4.png

由接口时序图可知,空闲电平可高可低,数据在第1个时钟上升沿采样。对应的可以选择用模式0或者模式3和MCU进行对接。

三、实例演示

本实例演示MCU(CW32L083)芯片对外部SPI FLASH(W25Q64JVSSIQ)进行读写访问,SPI接口选择模式0。

1.配置RCC和GPIO

void RCC_Configuration(void)
{
  RCC_HSI_Enable(RCC_HSIOSC_DIV2); //SYSCLK = HSI = 24MHz = HCLK = PCLK
  //外设时钟使能
  RCC_AHBPeriphClk_Enable(DEBUG_UART_GPIO_CLK | RCC_AHB_PERIPH_GPIOC, ENABLE);
  DEBUG_UART_APBClkENx(DEBUG_UART_CLK, ENABLE);
}
             
void GPIO_Configuration(void)//包含UART TX RX 复用和LED开关
{
  GPIO_InitTypeDef GPIO_InitStructure = {0};
  DEBUG_UART_AFTX; //UART TX RX 复用
  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);
  GPIO_InitStructure.Pins = GPIO_PIN_3 | GPIO_PIN_2; //PC3 LED1 / PC2 LED2
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
  PC03_SETLOW();//LED灭
  PC02_SETLOW();
}

2.配置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; // 传输时钟源UCLK
   UART_InitStructure.UART_UclkFreq = DEBUG_UART_UclkFreq; // 传输时钟UCLK频率
   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);
}

3.配置SPI接口

void SPI_FLASH_Init(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  /************************GPIO Configuration***********************/
  RCC_AHBPeriphClk_Enable(FLASH_SPI_SCK_GPIO_CLK | FLASH_SPI_MISO_GPIO_CLK | FLASH_SPI_MOSI_GPIO_CLK | FLASH_SPI_CS_GPIO_CLK, ENABLE);
  FLASH_SPI_APBClkENx(FLASH_SPI_CLK, ENABLE);
  //SPI SCK MOSI MISO 复用
  FLASH_SPI_AF_SCK;
  FLASH_SPI_AF_MISO;  
  FLASH_SPI_AF_MOSI;     
  //CS
  GPIO_InitStructure.Pins = FLASH_SPI_CS_GPIO_PIN;
  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStructure.IT = GPIO_IT_NONE;
  GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
  GPIO_Init(FLASH_SPI_CS_GPIO_PORT, &GPIO_InitStructure);
  //SCK   
  GPIO_InitStructure.Pins = FLASH_SPI_SCK_GPIO_PIN;
  GPIO_InitStructure.IT = GPIO_IT_NONE;
  GPIO_Init(FLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
  //MOSI
  GPIO_InitStructure.Pins = FLASH_SPI_MOSI_GPIO_PIN;
  GPIO_InitStructure.IT = GPIO_IT_NONE;
  GPIO_Init(FLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
  //MISO
  GPIO_InitStructure.Pins = FLASH_SPI_MISO_GPIO_PIN;
  GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
  GPIO_InitStructure.IT = GPIO_IT_NONE;
  GPIO_Init(FLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
   //拉高CS
  FLASH_SPI_CS_HIGH();
  /************************SPI Configuration***********************/
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;    // 双线全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;              // 主机模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;   // 帧数据长度为8bit
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;       // 时钟空闲电平为低
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;     // 第1边沿采样
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;        // 片选信号由SSI寄存器控制
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; 
  // 波特率为PCLK的8分频
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    // 最高有效位 MSB 收发在前
  SPI_InitStructure.SPI_Speed = SPI_Speed_High;    // 低速SPI
  SPI_Init(FLASH_SPIx, &SPI_InitStructure);
}

4.比较两个缓冲区数据

TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
  while(BufferLength--)
  {
     if(*pBuffer1 != *pBuffer2)
     {
       return FAILED;                 
     }
     pBuffer1++;
     pBuffer2++;               
  }
  return PASSED;
}

5.将C库printf函数重新定位到UART

PUTCHAR_PROTOTYPE
{
   UART_SendData_8bit(DEBUG_UARTx, (uint8_t)ch);// 通过UARTx发送一个数据(8bit)
   while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET);
   return ch;
}
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
   size_t nChars = 0;     
                 
   if (buffer == 0)
   {//应该刷新内部缓冲区 "handle" == -1
     return 0;
   }
   for (size != 0; --size)
   {
     UART_SendData_8bit(DEBUG_UARTx, *buffer++);
     while (UART_GetFlagStatus(DEBUG_UARTx, UART_FLAG_TXE) == RESET);
     ++nChars;
   }
   return nChars;
}

6.主程序实现SPI对FLASH的读写(具体对FLASH读写可参考cw32_eval_spi_flash.c)

int32_t main(void)
{ 
  RCC_Configuration();//配置RCC
  GPIO_Configuration();//配置GPIO
  UART_Configuration();//配置UART
  SPI_FLASH_Init();//配置SPI
  printf("\r\nCW32L083 SPI FLASH Example\r\n");
  DeviceID = SPI_FLASH_DeviceID();//读取Device ID
  ManufactDeviceID = SPI_FLASH_ManufactDeviceID();//读取Manufacturer / Device ID
  JedecID = SPI_FLASH_JedecID();//读取JEDEC ID
  SPI_FLASH_UniqueID(UniqueID);// 读取Unique ID (64bit)
  printf("\r\nDeviceID = 0x%X\r\nManufactDeviceID = 0x%X\r\nJedecID = 0x%X", DeviceID, ManufactDeviceID, JedecID);
  printf("\r\nUniqueID = 0x ");
  for(uint8_t i = 0; i<8; i++)
  {
     printf("%X ", UniqueID[i]);
  }
  
  if(JedecID == sJedecID)  /* Check JEDEC ID */
  {
     printf("\r\n\nFLASH Detected\r\n");;
     SPI_FLASH_SectorErase(FLASH_SectorToEraseAddress); //擦除扇区 4KB
     SPI_FLASH_BufferWrite(TxBuffer, FLASH_WriteAddress, BufferSize); //写数据
     printf("\r\n写入的数据为:%s\r\n", TxBuffer);
     SPI_FLASH_BufferRead(RxBuffer, FLASH_ReadAddress, BufferSize); //读数据
     printf("\r\n读出的数据为:%s\r\n", RxBuffer);
     //检查写入的数据与读出的数据是否一致
     TransferStatus = Buffercmp(TxBuffer, RxBuffer, BufferSize);
     if(TransferStatus == PASSED)
     {
        PC03_SETHIGH();
        printf("\r\nFLASH Success\r\n");
     }
     else
     {
        PC02_SETHIGH();
        printf("\r\nFLASH Error 1\r\n");
     }
     SPI_FLASH_SectorErase(FLASH_SectorToEraseAddress); //擦除扇区 4KB
     SPI_FLASH_BufferRead(RxBuffer, FLASH_ReadAddress, BufferSize); //读数据
     for(uint8_t j = 0; j < BufferSize; j++)
     {
        if(RxBuffer[j] != 0xFF)
        {
           PC02_SETHIGH();
           printf("\r\nFLASH Error 2\r\n");
        }
     }                
   }
   else// (FlashID != sFLASH_ID)
   {
     PC02_SETHIGH();
     printf("\r\nFLASH Error 3\r\n");
   }
   while(1)
   {
   }
}

7.演示说明

程序读外部FLASH的DeviceID、ManufactDeviceID、JedecID、UniqueID并打印读取到的芯片相关信息,然后依次进行写入数据、读取数据、比较数据、擦除SECTOR、读取数据等操作,当读写正确时,通过UART打印“FLASH Success”,LED1亮。当读取的JedecID不正确,写入读取内容不一致,擦除后读取数据不为全FF时,打印“FLASH Error 2”,LED2亮。如下图所示:

5.png

关于CW32更多详细信息,请访问官方网站www.whxy.com。

来源:武汉芯源半导体

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

围观 239

串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间。CW32L083 内部集成 2 个串行外设 SPI 接口,支持双向全双工、单线半双工和单工通信模式,可配置 MCU 作为 主机或从机,支持多主机通信模式,支持直接内存访问(DMA)。

单工通信模式

SPI 支持单工通信模式,主机和从机通过一根单向数据线进行单发或单收通信。主机使用 MOSI 信号线进行单发通信,使用 MISO 信号线进行单收通信;从机使用 MOSI 信号线进行单收通信, 使用 MISO 信号线进行单发通信,未使用的信号线可供其它功能使用。 

主机单发,从机单收应用场景下,连接框图如下:

1.png

主机单发配置: 

设置 SPIx_CR1.MODE 为 0x1,SPI 工作于单工单发通信模式;

设置 SPIx_CR1.MSTR 为 1,SPI 工作于主机模式。 

设置 SPIx_SSI.SSI 为 0,在从机选择 CS 引脚输出低电平,作为通信起始信号。 

当发送缓冲器为空时,即 SPIx_ISR.TXE 标志位为 1,将待发送的一帧数据写入 SPIx_DR 寄存器,数据在同步移位时钟信号的控制下从 MOSI 引脚输出。 

当写入最后一帧数据后,必须等待发送缓冲空标志位 SPIx_ISR.TXE 变为 1,同时 SPI 总线忙标志位 SPIx_ISR. BUSY 变为 0,以确保数据发送完毕。然后设置 SPIx_SSI.SSI 为 1,使从机选择 CS 引脚输出高电平,结束本次通信。 

从机单收配置: 

设置 SPIx_CR1.MODE 为 0x2,SPI 工作于单工单收通信模式;

设置 SPIx_CR1.MSTR 为 0,SPI 工作于从机模式。 

当检测到 CS 引脚变为低电平时,从机开始与主机通信。当接收缓冲器非空时,即 SPIx_ISR.RXNE 标志位为 1,表示已经接收完成一帧数据,此时可以读取 SPIx_DR 寄存器。当检测到 CS 引脚变为高电平时,本次通信结束。

主机单收,从机单发应用场景下,连接框图如下:

2.png

具体设置与主机单发和从机单收类似,详情可查看用户手册。

SPI中断

SPI 控制器支持 8 个中断源,当 SPI 中断触发事件发生时,中断标志位会被硬件置位,如果设置了对应的中断使能控制位,将产生中断请求。 

3.png

在用户 SPI 中断服务程序中,应查询相关 SPI 中断标志位,以进行相应的处理,在退出中断服务程序之前,要清除该中断标志位,避免重复进入中断程序。

实例演示: SPI单工模式进行主从机通信,主机单发,从机单收。

SPIy(主机)采用中断方式发送TxBuffer缓冲区中的数据,SPIz(从机)采用中断方式接收数据并存储到RxBuffer缓冲区,比较TxBuffer和RxBuffer,如果数据一致LED1亮,否则LED2亮。

1. 配置RCC

void RCC_Configuration(void)
{
    RCC_HSI_Enable(RCC_HSIOSC_DIV2);  //SYSCLK = HSI = 24MHz = HCLK = PCLK
    RCC_AHBPeriphClk_Enable(SPIy_GPIO_CLK | SPIz_GPIO_CLK | RCC_AHB_PERIPH_GPIOC, ENABLE); //外设时钟使能
    SPIy_APBClkENx(SPIy_CLK, ENABLE);
    SPIz_APBClkENx(SPIz_CLK, ENABLE);
}‍

2. 配置GPIO

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    SPIy_AF_SCK; //SPI SCK MOSI 复用
    SPIy_AF_MOSI;
    SPIz_AF_SCK;
    SPIz_AF_MOSI;
    GPIO_InitStructure.Pins = SPIy_SCK_PIN; //推挽输出
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(SPIy_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = SPIy_MOSI_PIN;
    GPIO_Init(SPIy_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = SPIz_SCK_PIN; //浮空输入
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    GPIO_Init(SPIz_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = SPIz_MOSI_PIN;
    GPIO_Init(SPIz_GPIO, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = GPIO_PIN_3 | GPIO_PIN_2; //PC3 LED1 / PC2 LED2
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOC, &GPIO_InitStructure);
    PC03_SETLOW();//LED灭
    PC02_SETLOW();
}‍

3. 配置SPI

void SPI_Configuration()
{
    SPI_InitTypeDef SPI_InitStructure = {0};
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_TxOnly; // 单工单发
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 帧数据长度为8bit
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟空闲电平为低
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第一个边沿采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 片选信号由SSI寄存器控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 波特率为PCLK的8分频
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 最高有效位 MSB 收发在前
    SPI_InitStructure.SPI_Speed = SPI_Speed_Low; // 低速SPI
    SPI_Init(SPIy, &SPI_InitStructure);
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_RxOnly; // 单工单收
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; // 从机模式
    SPI_Init(SPIz, &SPI_InitStructure);
    SPI_Cmd(SPIy, ENABLE);
    SPI_Cmd(SPIz, ENABLE);
}‍

4. 配置NVIC中断函数

void NVIC_Configuration(void)
{
    NVIC_SetPriority(SPIy_IRQ, 1); //优先级,无优先级分组
    NVIC_SetPriority(SPIz_IRQ, 0);
    NVIC_EnableIRQ(SPIy_IRQ); //SPI中断使能
    NVIC_EnableIRQ(SPIz_IRQ);
}
void SPI1_IRQHandler(void)//SPI1中断函数
{
    if(SPI_GetITStatus(CW_SPI1, SPI_IT_TXE) != RESET)
    {
        if(TxCounter == BufferSize - 1)
        {
            SPI_ITConfig(CW_SPI1, SPI_IT_TXE, DISABLE);
        }
        SPI_SendData(CW_SPI1, TxBuffer[TxCounter++]);
    }
}
void SPI2_IRQHandler(void) //SPI2中断函数
{
    if(SPI_GetITStatus(CW_SPI2, SPI_IT_RXNE) != RESET)
    {
        RxBuffer[RxCounter++] = SPI_ReceiveData(CW_SPI2);
    }
}‍

5. 比较两个buffers区

TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
    while(BufferLength--)
  {
    if(*pBuffer1 != *pBuffer2)
        {
            return FAILED;
        }
        pBuffer1++;
        pBuffer2++;
    }
    return PASSED;
}‍

6. 主程序

int32_t main(void)
{
    RCC_Configuration();//配置RCC
    GPIO_Configuration();//配置GPIO
    SPI_Configuration();//配置SPI
    NVIC_Configuration();//配置NVIC

    SPI_ITConfig(SPIz, SPI_IT_RXNE, ENABLE); //使能 SPIz RXNE 中断
    SPI_NSSInternalSoftwareConfig(SPIz, SPI_NSSInternalSoft_Reset); //软件NSS,选中SPIz
    SPI_ITConfig(SPIy, SPI_IT_TXE, ENABLE); //使能 SPIy TXE 中断
    while(RxCounter < BufferSize); //等待收发完成
    SPI_NSSInternalSoftwareConfig(SPIz, SPI_NSSInternalSoft_Set); //释放SPIz
    TransferStatus = Buffercmp(TxBuffer, RxBuffer, BufferSize); //检查收发数据一致性
    if(TransferStatus == PASSED) //PASSED
    {
        PC03_SETHIGH();//LED1亮
    }

    else //FAILED
    {
        PC02_SETHIGH();//LED2亮
    }
    while(1)
    {
    }
}‍

7.实验结果显示:LED1亮起,buffers区数据相等,SPI单工模式主从通讯(中断方式)功能实现。

来源:武汉芯源半导体

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

围观 175

以CW32L083为例,其定时器分为6种:

基本定时器:CW32L083 内部集成 3 个基本定时器 (BTIM),每个 BTIM 完全独立且功能完全相同,各包含一个 16bit 自动重装 载计数器并由一个可编程预分频器驱动。BTIM 支持定时器模式、计数器模式、触发启动模式和门控模式 4 种工作模式,支持溢出事件触发中断请求和 DMA 请求。得益于对触发信号的精细处理设计,使得 BTIM 可以由硬件自 动执行触发信号的滤波操作,还能令触发事件产生中断和 DMA 请求。

低功耗定时器:CW32L083 内部集成 1 个 16 位低功耗定时器(LPTIM),可以以很低的功耗实现定时或对外部脉冲计数的功能。通过选择合适的时钟源和触发信号,可以实现系统低功耗休眠时将MCU唤醒的功能。LPTIM 内部具有一个比较寄存器,可实现比较输出和 PWM 输出,并可以控制输出波形的极性。此外,LPTIM 还可以与正交编码器连接,自动 实现递增计数和递减计数。

通用定时器:CW32L083 内部集成 4 个通用定时器(GTIM),每个 GTIM 完全独立且功能完全相同,各包含一个16bit 自动重 装载计数器并由一个可编程预分频器驱动。GTIM 支持定时器模式、计数器模式、触发启动模式和门控模式 4 种基本工作模式,每组带4 路独立的捕获 / 比较通道,可以测量输入信号的脉冲宽度(输入捕获)或者产生输出波形(输出比较和 PWM)。

高级定时器:高级定时器 (ATIM) 由一个 16 位的自动重载计数器和 7 个比较单元组成,并由一个可编程的预分频器驱动。ATIM 支持 6 个独立的捕获 / 比较通道,可实现 6 路独立 PWM 输出或 3 对互补 PWM 输出或对 6 路输入进行捕获。可 用于基本的定时 / 计数、测量输入信号的脉冲宽度和周期、产生输出波形(PWM、单脉冲、插入死区时间的互补 PWM 等)。

独立看门狗定时器 (IWDT):CW32L083 内部集成独立看门狗定时器 (IWDT),使用专门的内部 RC 时钟源 RC10K,可避免运行时受到外部因素 影响。一旦启动 IWDT,用户需要在规定时间间隔内对 IWDT 的计数器进行重载,否则计数器溢出会触发复位或 产生中断信号。IWDT 启动后,可停止计数。可选择在深度休眠模式下 IWDT 保持运行或暂停计数。专门设置的键值寄存器,可以锁定 IWDT 的关键寄存器,防止寄存器被意外修改。

窗口看门狗定时器 (WWDT):CW32L083 内部集成窗口看门狗定时器 (WWDT),用户需要在设定的时间窗口内进行刷新,否则将触发系统复位。WWDT 通常被用来监测有严格时间要求的程序执行流程,防止由外部干扰或未知条件造成应用程序的执行异常, 导致发生系统故障。

01、CW32定时器中断

定时器中断是由CW32中的定时器引起的中断,所谓中断就是程序正常顺序执行的时候,出现了突发事件,CPU停止当前的程序的执行,转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。

02、CW32定时器中断源

定时器主要中断源包括如下:

基本定时器(BTIM): 计数器(ARR/TOP)溢出中断、计数器触发中断。

低功耗定时器(LPTIM): 计数方向反向中断、ARR更新完成中断、比较寄存器更新完成中断、计数器触发中断、ARR自动重载匹配中断、比较匹配中断。

通用定时器(GTIM): 编码器计数方向变化中断、CHx捕获比较中断、计数器下溢中断、计数器触发中断、计数器ARR溢出中断。

高级定时器(ATIM): CHxA/B捕获比较中断、CHxA/B捕获数据丢失中断、计数器上溢中断、计数器下溢中断、刹车中断、CH4比较匹配中断、事件更新中断。

更新事件, 触发事件。

独立看门狗定时器(IWDT): 计数器溢出中断。

窗口看门狗定时器(WWDT):计数器溢出中断。

各中断源的含义详细描述参见对应产品的用户手册内容。

03、实际操作

以CW32L083为例,控制基本定时器BTIM1以固定的时间间隔产生中断,并在ARR溢出中断中控制口线电平翻转。

1. RCC时钟初始化

void RCC_Configuration(void)
{
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);

    //系统时钟设置为HSI时钟6分频,8MHz, PCLK、HCLK不分频,PCLK=HCLK=SysClk=8MHz

    __RCC_BTIM_CLK_ENABLE();

    __RCC_GPIOB_CLK_ENABLE();

}

2.初始化GPIO口

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pins = GPIO_PIN_8;
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
}

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

void NVIC_Configuration(void)
{
     __disable_irq();
     NVIC_EnableIRQ(BTIM1_IRQn);
    __enable_irq();
}

4.主函数

int32_t main(void)
{
    BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct = {0};   
    /* System Clocks Configuration */
    RCC_Configuration();

    /* NVIC Configuration */
     NVIC_Configuration();

     /* GPIO Configuration */
    GPIO_Configuration();

    BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_Mode_TIMER;
    /* 工作模式,00:定时器模式01:计数器模式10:触发计数模式11:门控计数模式  */
    BTIM_TimeBaseInitStruct.BTIM_Period = 49999;
    /* 计数重载周期,取值范围0x0000 到 0xFFFF.  */
    BTIM_TimeBaseInitStruct.BTIM_Prescaler = 7;    // 8分频
    /*8分频, 预分配系数,取值范围2的n次幂,n=0,1,2…15 */
    BTIM_TimeBaseInit(CW_BTIM1, &BTIM_TimeBaseInitStruct);
    //BTIM基本定时器初始化
    BTIM_ITConfig(CW_BTIM1, BTIM_IT_OV, ENABLE);   // 使能BTIMx的溢出中断
    BTIM_Cmd(CW_BTIM1, ENABLE);
    /*BTIM1以1MHz时钟进行计数,设置ARR寄存器为49999,则BTIM1将每50ms溢出一次,并触发中断服务程序。*/
    while (1)
    {
        /* 中断服务程序中 PB8输出翻转 */
    }
}

5.中断函数:PB08输出翻转

void BTIM1_IRQHandler(void)
{
    /* USER CODE BEGIN */
    if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV))
    // 检查BTIM的状态寄存器的各状态位是否置位
    {
        BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV);  
        // 清除BTIM的状态寄存器的各状态位
        PB08_TOG();
        //PB08口信号翻转
    }

    /* USER CODE END */
}

6.实验验证

示波器检测PB08口的信号输出,示波器波形图呈周期性翻转。

来源:武汉芯源半导体

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

围观 24

脉冲宽度调制(PWM),即“Pulse Width Modulation”的缩写,简称脉宽调制,是在数字电路中达到模拟输出效果的一种手段,常见应用电机调速,照明灯调光等。

在MCU中,主要通过定时器单元来时实现PWM输出,以CW32L083VxTx为例,LPTIM,GTIM,ATIM都可以输出PWM信号。

低功耗寄存器(LPTIM)中,LPTIM 在连续模式下可以输出 PWM 波,在单次模式下可以输出单脉冲波或单次置位波形。连续模式下输出PWM波的周期和占空比由自动重载寄存器 LPTIM_ARR 和比较寄存器 LPTIM_CMP决定。

通用定时器(GTIM)中,通过设置输出比较功能,可以产生一个由重载寄存器 GTIMx_ARR 确定频率、由比较捕获寄存器 GTIMx_CCRy 确定占空比的PWM信号。每个GTIM对应有4个GTIMx_CCRy寄存器,可输出4路PWM信号。向 GTIMx_CCMR 寄存器中的 CCyM 位写入 0xE 或 0xF,能够独立地控制每个 CHy 输出PWM信号的波形。

●设置 GTIMx_CMMR.CCyM 为 0xE,当 GTIMx_CNT >= GTIMx_CCRy 时,CHy 通道输出高电平,否则输出低电平。如果 GTIMx_CCRy 中的比较值大于重载寄存器 GTIMx_ARR 的值,则 CHy 通道输出保持为低电平;如果 GTIMx_CCRy 中的比较值为 0,则 CHy 通道输出保持为高电平。

●设置 GTIMx_CMMR.CCyM 为 0xF,当 GTIMx_CNT < GTIMx_CCRy 时,CHy 通道输出高电平,否则输出低电平。如果 GTIMx_CCRy 中的比较值大于重载寄存器 GTIMx_ARR 的值,则 CHy 通道输出保持为高电平;如果 GTIMx_CCRy 中的比较值为 0,则 CHy 通道输出保持为低电平。

下图是 GTIMx_CMMR.CCyM 为 0xE、GTIMx_ARR 为 0x08 时PWM波形实例图:

1.png

高级定时器(ATIM)中有独立PWM输出模式和互补PWM输出两种模式。

●独立PWM模式可独立输出6路PWM,PWM的周期和占空比由重载寄存器ATIM_ARR和比较捕获寄存器ATIM_CHxCCRy寄存器确定。PWM 输出模式需要设置控制寄存器 ATIM_CR、滤波寄存器 ATIM_FLTR 和死区寄存器 ATIM_DTR,如下表所示:

2.png

另外比较通道 CHx 的 A 路可通过控制寄存器 ATIM_CR 的 PWM2S 位域配置为单点比较或双点比较 工作方式。在单点比较方式下,使用比较捕获寄存器 ATIM_CHxCCRA 控制比较输出;在双点比较方式下,使用 比较捕获寄存器 ATIM_CHxCCRA 和 ATIM_CHxCCRB 控制比较输出。比较通道的 B 路只能使用单点比较,由比较 捕获寄存器 ATIM_CHxCCRB 控制比较输出。

●互补PWM模式可输出3对互补输出的PWM波形,通常用于电机控制。设置控制寄存器 ATIM_CR 的 COMP 位域为 1 选择互补 PWM 输出模式,比较输出通道 CHxA 与通道 CHxB 产生一 对互补 PWM。在互补 PWM 输出模式下,通道 CHx 的 A 路控制输出信号, B 路比较捕获寄存器 CHxCCRB 不再控制 CHxB 输出,但仍可用作内部控制,比如触发 ADC 或 DMA。

另外互补 PWM 输出模式,也可通过控制寄存器 ATIM_CR 的 PWM2S 位域选择单点比较或双点比较工作方式:单点比 较时使用比较捕获寄存器 ATIM_CHxCCRA 控制比较输出;双点比较时使用比较捕获寄存器 ATIM_CHxCCRA 和 ATIM_CHxCCRB 控制比较输出。

实例演示

以CW32L083VxTx的通用定时器GTIM1为例,实现PWM输出例程:GTIM1的CH3通道(PB08)输出周期为500uS,占空比递增递减循环改变的PWM信号。

1.配置不同的系统时钟。

void RCC_Configuration(void)
{
    /* 0. HSI使能并校准 */
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);
    /* 1. 设置HCLK和PCLK的分频系数 */
    RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
    RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
    /* 2. 使能PLL,通过HSI倍频到48MHz */
    RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 6);     
    // PLL输出频率48MHz
    RCC_PLL_OUT();
    ///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
    ///< 当使用的时钟源HCLK大于48M,小于等于72MHz:设置FLASH 读等待周期为3 cycle    
    __RCC_FLASH_CLK_ENABLE();
    FLASH_SetLatency(FLASH_Latency_2);
    /* 3. 时钟切换到PLL */
    RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
    RCC_SystemCoreClockUpdate(48000000);
}

2.配置GPIO口

void GPIO_Configuration(void)                                                 
{
    /* PB08作为GTIM1的CH3 PWM 输出 */                                                                 
    __RCC_GPIOB_CLK_ENABLE();
    PB08_AFx_GTIM1CH3();
    PB08_DIGTAL_ENABLE();
    PB08_DIR_OUTPUT();
    PB08_PUSHPULL_ENABLE();
}

3.配置中断使能

void NVIC_Configuration(void)
{
    __disable_irq();
     NVIC_EnableIRQ(GTIM1_IRQn);
    __enable_irq();
}

4.配置GTIM为PWM输出功能

void PWM_OutputConfig(void)
{
    GTIM_InitTypeDef GTIM_InitStruct = {0};
    __RCC_GTIM1_CLK_ENABLE();
     GTIM_InitStruct.Mode = GTIM_MODE_TIME; /*!< GTIM的模式选择。*/
    GTIM_InitStruct.OneShotMode = GTIM_COUNT_CONTINUE;
    /*!< GTIM的单次/连续计数模式选择。*/
    GTIM_InitStruct.Prescaler = GTIM_PRESCALER_DIV16; 
    /*!< GTIM的预分频系数。*/  
    // DCLK = PCLK / 16 = 48MHz/16 = 3MHz
    GTIM_InitStruct.ReloadValue = Period * 3 - 1; /*!< GTIM的重载值。*/
    //ARR设置为1499
    GTIM_InitStruct.ToggleOutState = DISABLE;
    GTIM_TimeBaseInit(CW_GTIM1, &GTIM_InitStruct);
    //GTIM的基础参数初始化
    GTIM_OCInit(CW_GTIM1, GTIM_CHANNEL3, GTIM_OC_OUTPUT_PWM_HIGH);
    //比较输出功能初始化
    GTIM_SetCompare3(CW_GTIM1, PosWidth);
    GTIM_ITConfig(CW_GTIM1, GTIM_IT_OV, ENABLE);
    GTIM_Cmd(CW_GTIM1, ENABLE);// GTIM使能
}

5.GTIM标志清零函数

void GTIM_ClearITPendingBit(GTIM_TypeDef *GTIMx, uint32_t GTIM_IT)
{
     GTIMx->ICR = ~GTIM_IT;
}

6.GTIM 比较值设置函数

void GTIM_SetCompare3(GTIM_TypeDef *GTIMx, uint32_t Value)
{
    GTIMx->CCR3 = 0x0000FFFF & Value;
}

7.GTIM中断处理函数

void GTIM1_IRQHandler(void)
{
    /* USER CODE BEGIN */
    // 中断每500us进入一次,每50ms改变一次PosWidth
    static uint16_t TimeCnt = 0;
    GTIM_ClearITPendingBit(CW_GTIM1, GTIM_IT_OV);
    if (TimeCnt++ >= 100)    // 50ms
    {
        TimeCnt = 0;
        if (Dir)
        {
            PosWidth += 15;    // 5us
        }
        else
        {
             PosWidth -= 15;
        }
        if (PosWidth >= Period * 3)
        {
            Dir = 0;
        }
        if (0 == PosWidth)
        {
            Dir = 1;
        }
            GTIM_SetCompare3(CW_GTIM1, PosWidth);
        }
             /* USER CODE END */
}

8.主函数 

uint32_t Period = 500;    // 周期,单位us
uint32_t PosWidth = 0;    // 正脉宽,单位us
uint8_t Dir = 1;    // 计数方向 1增加,0 减少

int32_t main(void)
{
    /*系统时钟配置 */
    RCC_Configuration();
    /* GPIO配置*/
    GPIO_Configuration();
    PWM_OutputConfig();
    /* NVIC配置*/
    NVIC_Configuration();
    while(1)
    {
        /* 中断服务程序见GTIM1_IRQHandler() */
}

9.实验演示

系统时钟由HSI提供,通过PLL倍频到48MHz。GTIM1经16分频后,以3MHz的频率计数,ARR设置为1499,GTIM1的溢出周期为500us。GTIM1每500us进入一次中断,每50ms改变一次CH3的CCR寄存器的值,即改变PWM的正脉宽,步长为5us,先递增到ARR,然后递减到0,如此反复。通过示波器图像显示,PB08处的信号波的占空比随时间进行周期性变化。截取2个波形如下:

3.png

4.png

来源:武汉芯源半导体

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

围观 257

页面

订阅 RSS - CW32