STM32入门学习5

本章主要讲解iic通信下串口改变占空比和频率

单独拿出来讲解的主要原因是,关于寄存器的配置。

在上一章节的实验当中,我们借助工具配置了9mhz的输出.h文件,但是现在要改变频率,就需要对具体的几个寄存器的值进行输入/改变。这也是最复杂的地方

1.经过查找我们可以得到

2.代码部分

1)(交给ai生成关于写入寄存器的内容)

c 复制代码
// I2C 句柄(假设你用的是 I2C1)
extern I2C_HandleTypeDef hi2c2;
#include "Si5351A.h"
// Si5351 的 I2C 7位从机地址
#define SI5351_DEV_ADDR_WRITE   (0x60 << 1)  // 0xC0
//计算并修改CLK输出频率
#include <stdint.h>
#include <stdbool.h>
// ============================================================
// 配置常量
// ============================================================
#define SI5351_XTAL_FREQ     27000000UL  // 晶振频率(Hz),根据实际修改

// ============================================================
// 底层 I2C 读写函数
// ============================================================
void Si5351_Write_Reg(uint8_t reg, uint8_t value)
{
    uint8_t tx_buffer[2];
    tx_buffer[0] = reg;
    tx_buffer[1] = value;
    HAL_I2C_Master_Transmit(&hi2c2, SI5351_DEV_ADDR_WRITE, tx_buffer, 2, 100);
}

uint8_t Si5351_Read_Reg(uint8_t reg)
{
    uint8_t value = 0;
    HAL_I2C_Mem_Read(&hi2c2, SI5351_DEV_ADDR_WRITE, reg, I2C_MEMADD_SIZE_8BIT, &value, 1, 100);
    return value;
}

// ============================================================
// 批量写入配置表
// ============================================================
#define SI5351_REGS_COUNT (sizeof(si5351a_revb_registers) / sizeof(si5351a_revb_registers[0]))

HAL_StatusTypeDef Si5351_WriteConfig(void)
{
    uint16_t i;
    HAL_StatusTypeDef status;
    uint8_t tx_buffer[2];

    for (i = 0; i < SI5351_REGS_COUNT; i++) {
        tx_buffer[0] = (uint8_t)(si5351a_revb_registers[i].address & 0xFF);
        tx_buffer[1] = si5351a_revb_registers[i].value;

        status = HAL_I2C_Master_Transmit(&hi2c2, SI5351_DEV_ADDR_WRITE, tx_buffer, 2, 100);

        if (status != HAL_OK) {
            return status;
        }
    }
    return HAL_OK;
}

// ============================================================
// 读取单个寄存器(带返回值)
// ============================================================
HAL_StatusTypeDef Si5351_ReadReg(uint8_t reg, uint8_t *value)
{
    return HAL_I2C_Mem_Read(&hi2c2, SI5351_DEV_ADDR_WRITE, reg, I2C_MEMADD_SIZE_8BIT, value, 1, 100);
}

// ============================================================
// 计算分频器 P1/P2/P3
// ============================================================
static void Si5351_Calc_Params(uint32_t a, uint32_t b, uint32_t c,
                               uint32_t *P1, uint32_t *P2, uint32_t *P3)
{
    if (b == 0) {
        *P3 = 1;
        *P2 = 0;
        *P1 = 128 * a - 512;
    } else {
        *P3 = c;
        *P2 = 128 * b - c * ((128 * b) / c);
        *P1 = 128 * a + ((128 * b) / c) - 512;
    }
}

// ============================================================
// 配置 PLLA(寄存器 26~33)
// ============================================================
static void Si5351_Config_PLLA(uint32_t pll_freq)
{
    uint32_t a, b, c;
    uint32_t P1, P2, P3;
    uint64_t temp;

    temp = (uint64_t)pll_freq * 1000000ULL / SI5351_XTAL_FREQ;
    a = (uint32_t)(temp / 1000000);
    b = (uint32_t)(temp % 1000000);
    c = 1000000;

    if (a < 15) { a = 15; b = 0; }
    if (a > 90) { a = 90; b = 0; }

    Si5351_Calc_Params(a, b, c, &P1, &P2, &P3);

    Si5351_Write_Reg(26, (P3 >> 8) & 0xFF);
    Si5351_Write_Reg(27, P3 & 0xFF);

    uint8_t reg28 = Si5351_Read_Reg(28) & 0xFC;
    reg28 |= (P1 >> 16) & 0x03;
    Si5351_Write_Reg(28, reg28);

    Si5351_Write_Reg(29, (P1 >> 8) & 0xFF);
    Si5351_Write_Reg(30, P1 & 0xFF);

    uint8_t reg31 = ((P3 >> 16) & 0x0F) << 4 | ((P2 >> 16) & 0x0F);
    Si5351_Write_Reg(31, reg31);

    Si5351_Write_Reg(32, (P2 >> 8) & 0xFF);
    Si5351_Write_Reg(33, P2 & 0xFF);
}

// ============================================================
// 配置 CLK0 输出分频器(寄存器 42~49)
// ============================================================
static void Si5351_Config_CLK0(uint32_t pll_freq, uint32_t output_freq, uint8_t r_div)
{
    uint32_t a, b, c;
    uint32_t P1, P2, P3;
    uint64_t temp;
    uint8_t reg_val;

    temp = (uint64_t)pll_freq * 1000000ULL / ((uint64_t)output_freq * (1 << r_div));
    a = (uint32_t)(temp / 1000000);
    b = (uint32_t)(temp % 1000000);
    c = 1000000;

    if (a < 6) { a = 6; b = 0; }
    if (a > 900) { a = 900; b = 0; }

    Si5351_Calc_Params(a, b, c, &P1, &P2, &P3);

    Si5351_Write_Reg(42, (P3 >> 8) & 0xFF);
    Si5351_Write_Reg(43, P3 & 0xFF);

    reg_val = (r_div << 5) & 0xE0;
    reg_val |= (P1 >> 16) & 0x03;
    Si5351_Write_Reg(44, reg_val);

    Si5351_Write_Reg(45, (P1 >> 8) & 0xFF);
    Si5351_Write_Reg(46, P1 & 0xFF);

    reg_val = ((P3 >> 16) & 0x0F) << 4 | ((P2 >> 16) & 0x0F);
    Si5351_Write_Reg(47, reg_val);

    Si5351_Write_Reg(48, (P2 >> 8) & 0xFF);
    Si5351_Write_Reg(49, P2 & 0xFF);
}

// ============================================================
// 复位 PLLA
// ============================================================
static void Si5351_Reset_PLLA(void)
{
    uint8_t reg177 = Si5351_Read_Reg(177);
    Si5351_Write_Reg(177, reg177 | 0x04);

    for (volatile int i = 0; i < 100; i++);

    Si5351_Write_Reg(177, reg177 & ~0x04);
}

// ============================================================
// 使能 CLK0 输出
// ============================================================
static void Si5351_Enable_CLK0(bool enable)
{
    uint8_t reg3 = Si5351_Read_Reg(3);
    if (enable) {
        reg3 &= ~0x01;
    } else {
        reg3 |= 0x01;
    }
    Si5351_Write_Reg(3, reg3);
}

// ============================================================
// 自动选择 R 分频值
// ============================================================
static uint8_t Si5351_Find_R_Div(uint32_t pll_freq, uint32_t output_freq)
{
    uint8_t r_div;
    uint32_t ms_div;

    for (r_div = 0; r_div <= 7; r_div++) {
        ms_div = pll_freq / (output_freq * (1 << r_div));
        if (ms_div <= 900) {
            break;
        }
    }

    if (r_div > 0) {
        ms_div = pll_freq / (output_freq * (1 << (r_div - 1)));
        if (ms_div < 6) {
            r_div--;
        }
    }

    if (r_div > 7) r_div = 7;
    return r_div;
}

// ============================================================
// 自动寻找最优 PLL 频率
// ============================================================
static uint32_t Si5351_Find_Best_PLL(uint32_t output_freq, uint8_t *r_div)
{
    uint32_t best_pll = 800000000;
    uint32_t best_error = 0xFFFFFFFF;
    uint32_t test_pll;
    uint8_t test_r;
    uint32_t ms_div;
    uint32_t actual_freq;
    uint32_t error;
    uint32_t fb_div;

    for (test_pll = 600000000; test_pll <= 900000000; test_pll += 1000000) {
        fb_div = test_pll / SI5351_XTAL_FREQ;
        if (fb_div < 15 || fb_div > 90) continue;

        test_r = Si5351_Find_R_Div(test_pll, output_freq);
        ms_div = test_pll / (output_freq * (1 << test_r));

        if (ms_div >= 6 && ms_div <= 900) {
            actual_freq = test_pll / (ms_div * (1 << test_r));
            error = (actual_freq > output_freq) ? (actual_freq - output_freq) : (output_freq - actual_freq);

            if (error < best_error) {
                best_error = error;
                best_pll = test_pll;
                *r_div = test_r;
            }
        }
    }

    return best_pll;
}

// ============================================================
// 设置 CLK0 频率
// ============================================================
void Si5351_Set_CLK0(uint32_t freq_hz)
{
    uint32_t pll_freq;
    uint8_t r_div;

    if (freq_hz == 0) return;

    pll_freq = Si5351_Find_Best_PLL(freq_hz, &r_div);

    Si5351_Config_PLLA(pll_freq);
    Si5351_Config_CLK0(pll_freq, freq_hz, r_div);

    uint8_t reg16 = Si5351_Read_Reg(16) & ~0x20;
    Si5351_Write_Reg(16, reg16);

    Si5351_Enable_CLK0(true);
    Si5351_Reset_PLLA();
}

// ============================================================
// 简化版:固定 PLL 频率
// ============================================================
void Si5351_Set_CLK0_Simple(uint32_t freq_hz)
{
    uint32_t pll_freq = 800000000;
    uint8_t r_div;

    if (freq_hz == 0) return;

    r_div = Si5351_Find_R_Div(pll_freq, freq_hz);

    Si5351_Config_PLLA(pll_freq);
    Si5351_Config_CLK0(pll_freq, freq_hz, r_div);

    uint8_t reg16 = Si5351_Read_Reg(16) & ~0x20;
    Si5351_Write_Reg(16, reg16);

    Si5351_Enable_CLK0(true);
    Si5351_Reset_PLLA();
}

// ============================================================
// 初始化 Si5351
// ============================================================
void Si5351_Init_CLK0(void)
{
    Si5351_Write_Reg(3, 0xFF);

    uint8_t reg16 = Si5351_Read_Reg(16) & ~0x20;
    Si5351_Write_Reg(16, reg16);

    uint8_t reg0;
    do {
        reg0 = Si5351_Read_Reg(0);
    } while (reg0 & 0x80);
}

// ============================================================
// 直接设置频率(最简调用)
// ============================================================
void Si5351_Init_and_Set_CLK0(uint32_t freq_hz)
{
    Si5351_Init_CLK0();
    Si5351_Set_CLK0(freq_hz);
}

2)在main函数中

c 复制代码
  Si5351_Init_CLK0();
	Si5351_Set_CLK0(10000000);
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); //启动pwm输出
	uint8_t per;
	uint8_t fre;
	uint8_t beg[] = "please input your CRR \r\n";
	uint8_t err[] = "wrong\r\n";
	uint8_t reg_value;  // ← 在这里声明变量
	int i;
	// 1. 写入配置(修正:加上括号)
	if (Si5351_WriteConfig() != HAL_OK) {
		// 错误处理
		while (1);
	}

	HAL_Delay(10);
	
	uint8_t R = 1;
	while (1) {
		if (R == 1) {
			HAL_UART_Transmit(&huart1, beg, sizeof(beg), 1000);
			R = 0;
		}
		if (R == 0) {
			HAL_StatusTypeDef status = HAL_UART_Receive(&huart1, &per,
					sizeof(per), HAL_MAX_DELAY);
			if (status == HAL_OK)
			{
				HAL_UART_Transmit(&huart1, &per, sizeof(per), 1000);
				__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, per);

				HAL_UART_Transmit(&huart1, (uint8_t*)"\r\n write down your Freq !\r\n", 18, 100);
				HAL_UART_Receive(&huart1, &fre,sizeof(fre), HAL_MAX_DELAY);
			    HAL_UART_Transmit(&huart1, &fre, sizeof(fre), 1000);
			    Si5351_Set_CLK0(fre * 1000000);

				R = 1;
			}
			else if (status != HAL_OK) {
				HAL_UART_Transmit(&huart1, err, sizeof(err), 1000);
				R = 1;
			}
		}
	}

3.实验现象

由于代码中没有设置二进制转换HEX输入的09=0x09是9

50====0x50也就是80,所以输出的频率9Mhz;占空比是80%

但是可以看出实验成功的

相关推荐
传奇开心果编程1 小时前
【Rust入门知识点学与练】第4课:条件判断 if / else
开发语言·学习·rust
新晨单片机设计1 小时前
S006A-基于STM32单片机温湿度检测系统【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus
lifallen1 小时前
Skill 的生命周期:问题消失以后
人工智能·学习·ai·ai编程
千谦阙听1 小时前
C++类和对象(中):默认成员函数、构造与析构、拷贝构造、运算符重载
开发语言·c++·学习
Agudamu11612 小时前
AI 视频学习笔记工具拆解:4款工具谁能把视频变成复习资料
人工智能·学习·音视频
HugoStudio_SWAN2 小时前
洛谷 B4500 / B4449 / B3843 凯撒密码、密码强度与密码合规——加密与安全的三道门
c++·学习·程序人生·算法·安全
星河单片机3 小时前
STM32驱动HS‑S37A水位传感器+OLED完整实验讲解
stm32·单片机·嵌入式硬件
代码不接地4 小时前
【EDA】0.公共部分&元件库设计
嵌入式硬件
一条破秋裤4 小时前
16_TIM编码器接口测速
笔记·stm32·学习