[{"content":"基本概念 拉格朗日乘子法（Lagrange Multipliers）是一种寻找多元函数在一组约束下的极值的方法。通过引入拉格朗日乘子，可以将有$d$个变量与$k$个约束条件的最优化问题转化为具有$d+k$个变量的无约束优化问题。1\n问题求解 等式约束 $$ \\min_\\mathbf x f(\\mathbf x)\\qquad\\text{s.t.}\\quad g(\\mathbf x)=0, $$ 约束曲面与极值曲面相切的点为极值点$\\mathbf x^\\ast$。\n对于约束曲面上的任意点$\\mathbf x$，该点的梯度$\\nabla g(\\mathbf x)$正交于约束曲面。 在最优点$\\mathbf x^\\ast$，目标函数在该点的梯度$\\nabla f(\\mathbf x^\\ast)$正交于约束曲面。 在最优点$\\mathbf x^\\ast$梯度$\\nabla g(\\mathbf x)$和$\\nabla f(\\mathbf x)$的方向必相同或相反，即存在$\\lambda\\ne 0$，使得$\\nabla f(\\mathbf x^\\ast)+\\lambda\\nabla g(\\mathbf x^\\ast)=0$ 构造拉格朗日函数$L(\\mathbf x,\\lambda)=f(\\mathbf x)+\\lambda g(\\mathbf x)$\n$\\nabla_\\mathbf xL(\\mathbf x,\\lambda)=0 \\Rightarrow \\nabla f(\\mathbf x^\\ast)+\\lambda\\nabla g(\\mathbf x^\\ast)=0$ $\\nabla_\\lambda L(\\mathbf x,\\lambda)=0 \\Rightarrow g(\\mathbf x)=0$ 不等式约束 $$ \\min_\\mathbf x f(\\mathbf x)\\qquad\\text{s.t.}\\quad g(\\mathbf x)\\le0, $$ 最优点在不等式约束的区域内时，等价于无约束问题。最优点在不等式约束边界上时，等价于等式约束问题。\n对于情况一，最优点在$g(\\mathbf x)\u0026lt;0$区域内，令$\\nabla f(\\mathbf x)=0$求解。即$\\nabla_\\mathbf xL(\\mathbf x, 0)=0$。 对于情况二，最优解在$g(\\mathbf x)=0$上，即存在$\\nabla f(\\mathbf x^\\ast)+\\lambda\\nabla g(\\mathbf x^\\ast)=0$。注意到若两个梯度方向相同，则还可再优化，$\\mathbf x$不是最优解。因此，需要取两个梯度方向相反，即$\\lambda \u0026gt; 0$。 结合上述两种情况，二者都满足$\\lambda g(\\mathbf x)=0$。因此，原优化问题可以转化成在KKT（Karush-Kuhn-Tucker）条件下的最小化拉格朗日函数 $$ L(\\mathbf x,\\lambda)=f(\\mathbf x)+\\lambda g(\\mathbf x) \\qquad\\text{s.t.}\\quad \\left\\{\\begin{align*} g(\\mathbf x)\\le0\\\\ \\lambda\\ge0\\\\ \\lambda g(\\mathbf x)=0 \\end{align*}\\right.. $$ 马轶荀. 优化-拉格朗日乘子法[EB/OL], (2020-07-03). https://zhuanlan.zhihu.com/p/154517678\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-03-18T00:00:00Z","permalink":"https://blog.detail.eu.org/p/ba2c58/","title":"拉格朗日乘子法"},{"content":"基本理论 动量法（Momentum） 普通梯度下降法$\\theta\\leftarrow\\theta-\\eta\\nabla_\\theta J(\\theta)$在接近最优值时收敛速度会变慢，有事甚至陷入局部最优。这时如果考虑历史梯度，将会引导参数向最优值更快收敛，这就是动量算法基本思想。\n$$ \\begin{align*} m\u0026\\leftarrow\\gamma m+\\eta\\nabla_\\theta J(\\theta) \\\\ \\theta\u0026\\leftarrow\\theta -m \\end{align*}. $$牛顿动量（Nesterov） 尽管动量法能最终找到最优解，但是在不同维度梯度差较大时容易震荡。牛顿动量法在超前一个动量单位处进行梯度更新，公式修正为\n$$ \\begin{align*} m\u0026\\leftarrow\\gamma m+\\eta\\nabla_\\theta J(\\theta+\\beta m) \\\\ \\theta\u0026\\leftarrow\\theta -m \\end{align*}. $$仿真 对二元函数$z=x^2-4xy+10y^2$分别用梯度下降法、动量法和牛顿动量法求最小值。$\\eta=0.012, \\gamma=0.8$。梯度下降方法速度慢，动量法震荡明显。\n结果 迭代结果和迭代次数 1 2 3 Gradient 最优点: (0.0000, 0.0000), 迭代次数: 816 Momentum 最优点: (0.0000, -0.0000), 迭代次数: 176 Nesterov 最优点: (0.0000, 0.0000), 迭代次数: 137 优化路径 梯度下降 动量法 牛顿动量 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 global A; %#ok % 此处仅作演示，实际编程过程中应避免使用全局变量。 A = [... 1, -2; ... -2, 10 ... ]; x_0 = [150, 100].\u0026#39;; eta = 1.2e-2; gamma = 0.8; max_iter = 1000; dim1_space = linspace(-200, 200, 1e2); dim2_space = linspace(-200, 200, 1e2); [X, Y] = meshgrid(dim1_space, dim2_space); Z = arrayfun(@(x, y) dst_function([x; y]), X, Y); % 三维曲面图 figure; surf(X, Y, Z, \u0026#39;EdgeColor\u0026#39;, \u0026#39;none\u0026#39;); colormap(jet); colorbar; xlabel(\u0026#39;x_1\u0026#39;); ylabel(\u0026#39;x_2\u0026#39;); [x_opt, x_history] = gradient_descent(x_0, eta, max_iter); % 绘制优化路径 figure; contour(X, Y, Z, 50); colormap(jet); colorbar; hold on; plot(x_history(1, :), x_history(2, :), \u0026#39;ro-\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 5); xlabel(\u0026#39;x_1\u0026#39;); ylabel(\u0026#39;x_2\u0026#39;); % 绘制最优点 hold on; plot(x_opt(1), x_opt(2), \u0026#39;kx\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 10); legend(\u0026#39;等高线\u0026#39;, \u0026#39;优化路径\u0026#39;, \u0026#39;最优点\u0026#39;); fprintf(\u0026#39;Gradient 最优点: (%.4f, %.4f), 迭代次数: %d\\n\u0026#39;, x_opt(1), x_opt(2), size(x_history, 2) - 1); [x_opt, x_history] = momentum_method(x_0, eta, gamma, max_iter); % 绘制优化路径 figure; contour(X, Y, Z, 50); colormap(jet); colorbar; hold on; plot(x_history(1, :), x_history(2, :), \u0026#39;ro-\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 5); xlabel(\u0026#39;x_1\u0026#39;); ylabel(\u0026#39;x_2\u0026#39;); % 绘制最优点 hold on; plot(x_opt(1), x_opt(2), \u0026#39;kx\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 10); legend(\u0026#39;等高线\u0026#39;, \u0026#39;优化路径\u0026#39;, \u0026#39;最优点\u0026#39;); fprintf(\u0026#39;Momentum 最优点: (%.4f, %.4f), 迭代次数: %d\\n\u0026#39;, x_opt(1), x_opt(2), size(x_history, 2) - 1); [x_opt, x_history] = nesterov_method(x_0, eta, gamma, max_iter); % 绘制优化路径 figure; contour(X, Y, Z, 50); colormap(jet); colorbar; hold on; plot(x_history(1, :), x_history(2, :), \u0026#39;ro-\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 5); xlabel(\u0026#39;x_1\u0026#39;); ylabel(\u0026#39;x_2\u0026#39;); % 绘制最优点 hold on; plot(x_opt(1), x_opt(2), \u0026#39;kx\u0026#39;, \u0026#39;LineWidth\u0026#39;, 2, \u0026#39;MarkerSize\u0026#39;, 10); legend(\u0026#39;等高线\u0026#39;, \u0026#39;优化路径\u0026#39;, \u0026#39;最优点\u0026#39;); fprintf(\u0026#39;Nesterov 最优点: (%.4f, %.4f), 迭代次数: %d\\n\u0026#39;, x_opt(1), x_opt(2), size(x_history, 2) - 1); function [x_opt, x_history] = gradient_descent(x_0, eta, max_iter) x_history = zeros(length(x_0), max_iter+1); x_history(:, 1) = x_0; x = x_0; for i = 1:max_iter grad = dst_function_gradient(x); x = x - eta * grad; x_history(:, i+1) = x; if norm(grad) \u0026lt; 1e-6 % 梯度足够小，认为收敛 x_history = x_history(:, 1:i+1); % 截断历史记录 break; end end x_opt = x; end function [x_opt, x_history] = momentum_method(x_0, eta, gamma, max_iter) x_history = zeros(length(x_0), max_iter+1); x_history(:, 1) = x_0; x = x_0; m = zeros(size(x_0)); for i = 1:max_iter grad = dst_function_gradient(x); m = gamma * m + eta * grad; x = x - m; x_history(:, i+1) = x; if norm(grad) \u0026lt; 1e-6 % 梯度足够小，认为收敛 x_history = x_history(:, 1:i+1); % 截断历史记录 break; end end x_opt = x; end function [x_opt, x_history] = nesterov_method(x_0, eta, gamma, max_iter) x_history = zeros(length(x_0), max_iter+1); x_history(:, 1) = x_0; x = x_0; m = zeros(size(x_0)); for i = 1:max_iter y = x - gamma * m; % 计算预测点 grad = dst_function_gradient(y); % 在预测点计算梯度 m = gamma * m + eta * grad; % 更新动量 x = x - m; % 更新参数 x_history(:, i+1) = x; if norm(grad) \u0026lt; 1e-6 % 梯度足够小，认为收敛 x_history = x_history(:, 1:i+1); % 截断历史记录 break; end end x_opt = x; end %% 目标函数 function y = dst_function(x) global A; %#ok y = x.\u0026#39; * A * x; end function grad = dst_function_gradient(x) global A; %#ok grad = 2 * A * x; end ","date":"2026-03-06T00:00:00Z","image":"https://blog.detail.eu.org/p/00a438/surf_hu_bec562dde2cb00be.png","permalink":"https://blog.detail.eu.org/p/00a438/","title":"Nesterov 牛顿动量"},{"content":"基本原理 迭代软阈值（Iterative Soft Thresholding, IST）利用LASSO回归模型将问题表述为 $$ \\arg\\min_\\mathbf x \\lVert{\\mathbf y-\\Phi\\mathbf x}\\rVert_2^2+\\lambda\\lVert \\mathbf x\\rVert_1, $$ 可以得到其迭代方法\n初始化：$\\mathbf x^{(0)}=0$或$\\mathbf x^{(0)}=\\Phi ^\\text T \\mathbf y$ 计算梯度下降：$\\mathbf r^{(k)}=\\mathbf x^{(k)}-t\\nabla\\lVert\\mathbf y-\\Phi \\mathbf x^{(k)}\\rVert_2^2$ 迭代优化直至收敛：$\\mathbf x^{(k+1)}=S_{\\lambda t}(\\mathbf r^{(k)})$ 其中$t$为步长，$S_\\omega(x)$表示软阈值函数，即 $$ [S_\\omega(x)]_i = \\left\\{ \\begin{align*} \u0026x_i-\\omega, \\quad x_i\u003e\\omega\\\\ \u00260, \\quad \\lvert x_i\\rvert\\le\\omega\\\\ \u0026x_i+\\omega, \\quad x_i\u003c-\\omega \\end{align*}\\right. $$ 推导过程 IST的推导需要使用近端梯度下降（PGD）方法，可以将原问题修改为 $$ \\mathbf x = \\arg\\min_\\mathbf x {\\frac12\\lVert A\\mathbf x-\\mathbf b\\rVert_2^2+\\lambda\\lVert\\mathbf x\\rVert_1}, $$ 令$f(\\mathbf x)=\\frac12\\lVert A\\mathbf x-\\mathbf b\\rVert_2^2+\\lambda\\lVert\\mathbf x\\rVert_1$，对其求导 $$ f^\\prime(\\mathbf x) = A^\\text T (A\\mathbf x-\\mathbf b)+\\lambda\\text{sgn}(\\mathbf x), $$ 求其最小值即令$f^\\prime(\\mathbf x)=0$。\n特殊情况$A=I$ 特别的当$A=I$时，$f^\\prime(\\mathbf x) = (\\mathbf x-\\mathbf b)+\\lambda\\text{sgn}(\\mathbf x)=0$，其解为软阈值函数 $$ \\mathbf x = S_\\lambda(\\mathbf b) = \\left\\{ \\begin{align*} \u0026b+\\lambda, \\quad b\u003c -\\lambda \\\\ \u00260, \\quad b\\le\\lvert\\lambda\\rvert \\\\ \u0026b-\\lambda, \\quad b\u003e\\lambda \\end{align*}\\right.. $$一般情况 回到一般情况，令$f(\\mathbf x) = \\lVert A\\mathbf x-\\mathbf b\\rVert^2_2$。对$f(\\mathbf x)$在$\\mathbf x^{(k)}$处做二阶泰勒展开，令$L=\\nabla^2f(\\mathbf x)=\\frac1t$ $$ \\begin{align*} \\arg\\min_\\mathbf x f(\\mathbf x) \u0026= \\arg\\min_\\mathbf x{f(\\mathbf x^{(k)})+\\nabla f(\\mathbf x^{(k)})^\\text T (\\mathbf x -\\mathbf x^{(k)})+\\frac12(\\mathbf x-\\mathbf x^{(k)})^\\text T\\nabla^2f(\\mathbf x^{(k)})(\\mathbf x-\\mathbf x^{(k)})} \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}\\sum_i{t^2[\\nabla f(\\mathbf x^{(k)})]_i^2+2t[\\nabla f(\\mathbf x^{(k)})]_i (x_i -x_i^{(k)})+(x_i-x_i^{(k)})^2} \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}{\\lVert\\mathbf x-[\\mathbf x^{(k)}-t\\nabla f(\\mathbf x^{(k)})]\\rVert^2_2} \\\\ \\end{align*}. $$ 此时，令$\\mathbf z^{(k)}=\\mathbf x^{(k)}-t\\nabla f(\\mathbf x^{(k)})$，原凸优化问题转换为$A=I$的形式（注意这里是$\\mathbf x^{(k+1)}=\\text{prox}_{h,t}(\\mathbf z^{(k)})$），于是有 $$ \\begin{align*} \\mathbf x^{(k+1)} \u0026= \\arg\\min_\\mathbf x \\frac1{2t}\\lVert\\mathbf x-\\mathbf z\\rVert^2_2+\\lambda\\lVert\\mathbf x\\rVert_1 \\\\ \u0026= \\arg\\min_\\mathbf x \\frac1{2}\\lVert\\mathbf x-\\mathbf z\\rVert^2_2+\\lambda t\\lVert\\mathbf x\\rVert_1 \\\\ \u0026= S_{\\lambda t}(\\mathbf z^{(k)}) \\end{align*}. $$步长选取 固定步长 假设函数$f(\\mathbf x) = \\lVert A\\mathbf x-\\mathbf b\\rVert^2_2$满足梯度上的Lipschitz连续。当步长$t\\le\\frac1L$时，能保证多次迭代后最终收敛至梯度为0的点，$L$表示$\\nabla f$的Lipschitz常数。 $$ L = \\sup\\frac{\\lVert\\nabla f(a)-\\nabla f(b)\\rVert_2}{\\lVert a-b\\rVert_2} $$ 令$a=\\mathbf x+\\mathbf x_0,b=\\mathbf x$则 $$ \\begin{align*} L \u0026= \\sup\\frac{\\lVert\\nabla f(\\mathbf x+\\mathbf x_0)-\\nabla f(\\mathbf x)\\rVert_2}{\\lVert\\mathbf x_0\\rVert_2} \\\\ \u0026= \\sup\\frac{\\lVert 2A^\\text T [A(\\mathbf x+\\mathbf x_0)-\\mathbf b]-2A^\\text T (A\\mathbf x-\\mathbf b)\\rVert_2}{\\lVert\\mathbf x_0\\rVert_2} \\\\ \u0026= \\sup\\frac{2\\lVert A^\\text TA\\mathbf x_0\\rVert_2}{\\lVert\\mathbf x_0\\rVert_2} \\\\ \u0026\\le\\frac{2\\lVert\\lambda_\\text{max}(A^\\text TA)\\mathbf x_0\\rVert_2}{\\lVert\\mathbf x_0\\rVert_2} \\\\ \u0026=2\\lambda_\\text{max}(A^\\text TA) \\end{align*} $$ 其中$\\lambda_\\text{max}(\\cdot)$表示取最大特征值，令$t=\\frac1L=\\frac1{2\\lambda_\\text{max}(A^\\text TA)}$\n回溯型步长 当矩阵$A$很大时，不容易计算$A^\\text TA$的特征值，或者不知道矩阵$A$时，可以通过回溯搜索的方式估计Lipschitz常数。将$f(\\mathbf x)$泰勒展开并放缩得到上界函数 $$ \\begin{align*} f(\\mathbf x) \u0026={f(\\mathbf x_0)+\\nabla f(\\mathbf x_0)^\\text T (\\mathbf x -\\mathbf x_0)+\\frac12(\\mathbf x-\\mathbf x_0)^\\text T\\nabla^2f(\\mathbf x_0)(\\mathbf x-\\mathbf x_0)}+o(x^3) \\\\ \u0026\\le{f(\\mathbf x_0)+\\nabla f(\\mathbf x_0)^\\text T (\\mathbf x -\\mathbf x_0)+\\frac L2(\\mathbf x-\\mathbf x_0)^\\text T(\\mathbf x-\\mathbf x_0)} \\\\ \u0026=Q(\\mathbf x,\\mathbf x_0;L) \\end{align*}, $$ 令$\\mathbf x=\\mathbf x^{(k+1)},\\mathbf x_0=x^{(k)}$，不断增大$\\hat L$，直到$f(\\mathbf x^{(k+1)})\\le Q(\\mathbf x^{(k+1)},\\mathbf x^{(k)};\\hat L)$满足。 1 2 3 4 5 6 7 8 9 \\begin{algorithm} \\caption{BackTracking} \\begin{algorithmic} \\STATE 初始化$L_0\u0026gt;0, \\eta\u0026gt;1, \\mathbf x_0\\in\\mathbb R^n$ \\REPEAT \\STATE $\\hat L = \\eta L_\\text{before}$ \\UNTIL{$f(\\mathbf x^{(k+1)})\\le Q(\\mathbf x^{(k+1)},\\mathbf x^{(k)};\\hat L)$} \\end{algorithmic} \\end{algorithm} ","date":"2026-03-04T00:00:00Z","permalink":"https://blog.detail.eu.org/p/b2bed5/","title":"IST"},{"content":"基本原理 近端梯度下降（Proximal Gradient Descent）是梯度下降方法的一种，其适用于目标函数存在不可微部分的凸优化问题。问题描述为 $$ \\mathbf x = \\arg\\min_\\mathbf x {g(\\mathbf x)+h(\\mathbf x)}, $$ 其中，$g(\\mathbf x)$为可微凸函数、$h(\\mathbf x)$为不可微凸函数。\n求解方法 求解该类型凸优化问题可以通过近端映射（Proximal Mapping）和梯度迭代的手段实现。近端映射函数定义为 $$ \\text{prox}_{h,t}(\\mathbf x) = \\arg\\min_\\mathbf z{\\frac1{2t}\\lVert\\mathbf x-\\mathbf z\\rVert^2_2+h(\\mathbf z)}, $$ 梯度迭代方法为 $$ \\begin{align*} \u0026\\mathbf z^{(k)}=\\mathbf x^{(k)}-t_k\\nabla g(\\mathbf x^{(k)}) \\\\ \u0026\\mathbf x^{(k+1)}=\\text{prox}_{h,t_k}(\\mathbf z^{(k)}) \\end{align*} $$证明 对$g(\\mathbf x)$在$\\mathbf x^{(k)}$处做二阶泰勒展开，令$L=\\nabla^2g(\\mathbf x)=\\frac1t$ $$ \\begin{align*} \\arg\\min_\\mathbf x g(\\mathbf x)+h(\\mathbf x) \u0026\\approx \\arg\\min_\\mathbf x{g(\\mathbf x^{(k)})+\\nabla g(\\mathbf x^{(k)})^\\text T (\\mathbf x -\\mathbf x^{(k)})+\\frac12(\\mathbf x-\\mathbf x^{(k)})^\\text T\\nabla^2g(\\mathbf x^{(k)})(\\mathbf x-\\mathbf x^{(k)})}+h(\\mathbf x) \\\\ \\mathbf{\\hat x}\u0026= \\arg\\min_\\mathbf x{\\nabla g(\\mathbf x^{(k)})^\\text T (\\mathbf x -\\mathbf x^{(k)})+\\frac1{2t}(\\mathbf x-\\mathbf x^{(k)})^\\text T(\\mathbf x-\\mathbf x^{(k)})}+h(\\mathbf x) \\\\ \u0026= \\arg\\min_\\mathbf x\\sum_i{[\\nabla g(\\mathbf x^{(k)})]_i (x_i -x_i^{(k)})+\\frac1{2t}(x_i-x_i^{(k)})^2}+h(\\mathbf x) \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}\\sum_i{2t[\\nabla g(\\mathbf x^{(k)})]_i (x_i -x_i^{(k)})+(x_i-x_i^{(k)})^2}+h(\\mathbf x) \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}\\sum_i{t^2[\\nabla g(\\mathbf x^{(k)})]_i^2+2t[\\nabla g(\\mathbf x^{(k)})]_i (x_i -x_i^{(k)})+(x_i-x_i^{(k)})^2}+h(\\mathbf x) \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}\\sum_i{[x_i-x_i^{(k)}+t[\\nabla g(\\mathbf x^{(k)})]_i]^2}+h(\\mathbf x) \\\\ \u0026= \\arg\\min_\\mathbf x\\frac1{2t}{\\lVert\\mathbf x-[\\mathbf x^{(k)}-t\\nabla g(\\mathbf x^{(k)})]\\rVert^2_2}+h(\\mathbf x) \\\\ \\end{align*}. $$ 令$\\mathbf z^{(k)}=\\mathbf x^{(k)}-t\\nabla g(\\mathbf x^{(k)})$，原问题即可转换为近端映射 $$ \\begin{align*} \\mathbf{\\hat x} \u0026= \\arg\\min_\\mathbf x\\frac1{2t}{\\lVert\\mathbf x-\\mathbf z\\rVert^2_2}+h(\\mathbf x) \\\\ \u0026=\\text{prox}_{h,t}(\\mathbf z) \\end{align*}. $$ 设定合适的参数，即可使用迭代方法求得$\\arg\\min_\\mathbf x g(\\mathbf x)+h(\\mathbf x)$的结果。\nLipschitz常数 假定目标凸函数满足梯度上的Lipschitz连续。当步长$t\\le\\frac1L$时，保证多次迭代后能收敛到梯度为0的点。梯度Lipschitz连续表示为 $$ \\frac{\\lVert\\nabla f(a)-\\nabla f(b)\\rVert_2}{\\lVert a-b\\rVert_2} \\le L(f) \\quad \\forall a,b\\in\\text{dom}f, $$ 函数$\\nabla f$的Lipschitz常数为 $$ L = \\sup\\frac{\\lVert\\nabla f(a)-\\nabla f(b)\\rVert_2}{\\lVert a-b\\rVert_2} $$","date":"2026-03-04T00:00:00Z","permalink":"https://blog.detail.eu.org/p/ac3f52/","title":"PGD"},{"content":"基本原理 迭代硬阈值算法（Iterative Hard Thresholding, IHT），在提出时称M-Sparse Algorithm。$M$稀疏问题可以表示为 $$ \\min_\\mathbf y\\lVert \\mathbf x - \\Phi\\mathbf y \\rVert^2_2\\quad \\text{s.t.}\\quad \\lVert\\mathbf y\\rVert_0\\le M, $$ 可以得到其迭代算法 $$ \\mathbf y^{n+1}=H_M(\\mathbf y^n+\\Phi^\\rm H(\\mathbf x-\\Phi\\mathbf y^n)), $$ 其中，$H_M$为非线性算子，其返回$M$维最大幅值 $$ H_M(y_i)= \\left\\{ \\begin{align} 0\\quad \\lvert y_i\\rvert\\lt\\lambda^{0.5}_M(\\mathbf y)\\\\ y_i\\quad \\lvert y_i\\rvert\\ge\\lambda^{0.5}_M(\\mathbf y) \\end{align} \\right.. $$推导过程 替代目标函数（Surrogate Objective Function） $$ C_M^S(\\mathbf{y,z})=\\lVert \\mathbf{x-\\Phi y} \\rVert_2^2 - \\lVert \\mathbf{\\Phi y-\\Phi z} \\rVert_2^2 + \\lVert \\mathbf{y-z} \\rVert_2^2 \\qquad \\lVert\\mathbf\\Phi\\rVert_2\\lt1, $$ 当$\\mathbf{y=z}$时，该函数即为原目标函数，其余情况下均大于目标函数。$\\lVert\\mathbf\\Phi\\rVert_2\\lt1$由$0\\lt eig(\\mathbf I-\\mathbf \\Phi^\\rm{H} \\mathbf\\Phi)\\lt 1$推导出。\n替代目标函数变形 $$ C_M^S(\\mathbf{y,z})=\\sum_i[y_i^2-2y_i(z_i+\\phi_i^\\rm{T}\\mathbf x-\\phi_i^\\rm{T}\\mathbf{\\Phi z})]+\\lVert\\mathbf x\\rVert_2^2+\\lVert\\mathbf z\\rVert_2^2+\\lVert\\mathbf{\\Phi z}\\rVert_2^2, $$ 由于优化目标为$\\mathbf y$，而后面三项与其无关。因此，对其优化时忽略这三项不影响结果。 $$ C_M^S(\\mathbf{y,z})\\propto\\sum_i[y_i^2-2y_i(z_i+\\phi_i^\\rm{T}\\mathbf x-\\phi_i^\\rm{T}\\mathbf{\\Phi z})]. $$极值点获取 $$ y_i^*=z_i+\\phi_i^\\rm H\\mathbf x-\\phi_i^\\rm H\\mathbf{\\Phi z}, $$ 极值点通过简单的配方法即可获得。这时，取得最小值$\\sum_i{-(y_i^*)^2}$。\n迭代公式获取 考虑到$\\lVert\\mathbf y\\rVert_0\\le M$的约束，需要保留$\\lVert\\mathbf y^*\\rVert$的最大$M$项，即使用硬阈值函数。\n代码 简单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 function [s_est, info] = iht(g, Phi, max_iter, K) [~, N] = size(Phi); s_est = zeros(N, 1); g = g(:); g_norm = norm(g); L = compute_lipschitz_constant(Phi); t = 1 / L; % 计算步长 residual_tol = 1e-2; plateau_tol = 1e-6; plateau_patience = 20; plateau_count = 0; residual = Phi * s_est - g; % 初始残差 residual_norm = norm(residual); stop_reason = \u0026#34;max_iter\u0026#34;; iter_done = 0; for iter = 1:max_iter z = s_est - Phi\u0026#39; * residual * t; % 计算梯度步长更新 s_est = hard_thresholding(z, K); % 应用硬阈值处理 prev_residual_norm = residual_norm; residual = Phi * s_est - g; % 更新残差 residual_norm = norm(residual); iter_done = iter; if residual_norm \u0026lt; residual_tol * g_norm stop_reason = \u0026#34;residual_tol\u0026#34;; break; end improvement = (prev_residual_norm - residual_norm) / max(prev_residual_norm, eps); if improvement \u0026lt; plateau_tol plateau_count = plateau_count + 1; else plateau_count = 0; end if plateau_count \u0026gt;= plateau_patience stop_reason = \u0026#34;plateau\u0026#34;; break; end end if nargout \u0026gt; 1 info = struct(); info.iter = iter_done; info.residual_norm = residual_norm; info.relative_residual = residual_norm / max(g_norm, eps); info.stop_reason = stop_reason; info.step_size = t; info.L = L; info.plateau_count = plateau_count; end end function r = hard_thresholding(x, K) % 对向量x进行硬阈值处理，保留最大的K个元素 r = zeros(size(x)); [~, idx] = sort(abs(x), \u0026#39;descend\u0026#39;); r(idx(1:K)) = x(idx(1:K)); end function L = compute_lipschitz_constant(Phi) ATA = Phi\u0026#39; * Phi; L = max(eig(ATA)); end 原文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 function [s, err_mse, iter_time]=hard_l0_Mterm(x,A,m,M,varargin) % hard_l0_Mterm: Hard thresholding algorithm that keeps exactly M elements % in each iteration. % % This algorithm has certain performance guarantees as described in [1], % [2] and [3]. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Usage % % [s, err_mse, iter_time]=hard_l0_Mterm(x,P,m,M,\u0026#39;option_name\u0026#39;,\u0026#39;option_value\u0026#39;) % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Input % % Mandatory: % x Observation vector to be decomposed % P Either: % 1) An nxm matrix (n must be dimension of x) % 2) A function handle (type \u0026#34;help function_format\u0026#34; % for more information) % Also requires specification of P_trans option. % 3) An object handle (type \u0026#34;help object_format\u0026#34; for % more information) % m length of s % M non-zero elements to keep in each iteration % % Possible additional options: % (specify as many as you want using \u0026#39;option_name\u0026#39;,\u0026#39;option_value\u0026#39; pairs) % See below for explanation of options: %__________________________________________________________________________ % option_name | available option_values | default %-------------------------------------------------------------------------- % stopTol | number (see below) | 1e-16 % P_trans | function_handle (see below) | % maxIter | positive integer (see below) | n^2 % verbose | true, false | false % start_val | vector of length m | zeros % step_size | number | 0 (auto) % % stopping criteria used : (OldRMS-NewRMS)/RMS(x) \u0026lt; stopTol % % stopTol: Value for stopping criterion. % % P_trans: If P is a function handle, then P_trans has to be specified and % must be a function handle. % % maxIter: Maximum number of allowed iterations. % % verbose: Logical value to allow algorithm progress to be displayed. % % start_val: Allows algorithms to start from partial solution. % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Outputs % % s Solution vector % err_mse Vector containing mse of approximation error for each % iteration % iter_time Vector containing computation times for each iteration % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Description % % Implements the M-sparse algorithm described in [1], [2] and [3]. % This algorithm takes a gradient step and then thresholds to only retain % M non-zero elements. It allows the step-size to be calculated % automatically as described in [3] and is therefore now independent from % a rescaling of P. % % % References % [1] T. Blumensath and M.E. Davies, \u0026#34;Iterative Thresholding for Sparse % Approximations\u0026#34;, submitted, 2007 % [2] T. Blumensath and M. Davies; \u0026#34;Iterative Hard Thresholding for % Compressed Sensing\u0026#34; to appear Applied and Computational Harmonic % Analysis % [3] T. Blumensath and M. Davies; \u0026#34;A modified Iterative Hard % Thresholding algorithm with guaranteed performance and stability\u0026#34; % in preparation (title may change) % See Also % hard_l0_reg % % Copyright (c) 2007 Thomas Blumensath % % The University of Edinburgh % Email: thomas.blumensath@ed.ac.uk % Comments and bug reports welcome % % This file is part of sparsity Version 0.4 % Created: April 2007 % Modified January 2009 % % Part of this toolbox was developed with the support of EPSRC Grant % D000246/1 % % Please read COPYRIGHT.m for terms and conditions. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Default values and initialisation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% [n1 n2]=size(x); if n2 == 1 n=n1; elseif n1 == 1 x=x\u0026#39;; n=n2; else error(\u0026#39;x must be a vector.\u0026#39;); end sigsize = x\u0026#39;*x/n; oldERR = sigsize; err_mse = []; iter_time = []; STOPTOL = 1e-16; MAXITER = n^2; verbose = false; initial_given=0; s_initial = zeros(m,1); MU = 0; if verbose display(\u0026#39;Initialising...\u0026#39;) end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Output variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% switch nargout case 3 comp_err=true; comp_time=true; case 2 comp_err=true; comp_time=false; case 1 comp_err=false; comp_time=false; case 0 error(\u0026#39;Please assign output variable.\u0026#39;) otherwise error(\u0026#39;Too many output arguments specified\u0026#39;) end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Look through options %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Put option into nice format Options={}; OS=nargin-4; c=1; for i=1:OS if isa(varargin{i},\u0026#39;cell\u0026#39;) CellSize=length(varargin{i}); ThisCell=varargin{i}; for j=1:CellSize Options{c}=ThisCell{j}; c=c+1; end else Options{c}=varargin{i}; c=c+1; end end OS=length(Options); if rem(OS,2) error(\u0026#39;Something is wrong with argument name and argument value pairs.\u0026#39;) end for i=1:2:OS switch Options{i} case {\u0026#39;stopTol\u0026#39;} if isa(Options{i+1},\u0026#39;numeric\u0026#39;) ; STOPTOL = Options{i+1}; else error(\u0026#39;stopTol must be number. Exiting.\u0026#39;); end case {\u0026#39;P_trans\u0026#39;} if isa(Options{i+1},\u0026#39;function_handle\u0026#39;); Pt = Options{i+1}; else error(\u0026#39;P_trans must be function _handle. Exiting.\u0026#39;); end case {\u0026#39;maxIter\u0026#39;} if isa(Options{i+1},\u0026#39;numeric\u0026#39;); MAXITER = Options{i+1}; else error(\u0026#39;maxIter must be a number. Exiting.\u0026#39;); end case {\u0026#39;verbose\u0026#39;} if isa(Options{i+1},\u0026#39;logical\u0026#39;); verbose = Options{i+1}; else error(\u0026#39;verbose must be a logical. Exiting.\u0026#39;); end case {\u0026#39;start_val\u0026#39;} if isa(Options{i+1},\u0026#39;numeric\u0026#39;) \u0026amp;\u0026amp; length(Options{i+1}) == m ; s_initial = Options{i+1}; initial_given=1; else error(\u0026#39;start_val must be a vector of length m. Exiting.\u0026#39;); end case {\u0026#39;step_size\u0026#39;} if isa(Options{i+1},\u0026#39;numeric\u0026#39;) \u0026amp;\u0026amp; (Options{i+1}) \u0026gt; 0 ; MU = Options{i+1}; else error(\u0026#39;Stepsize must be between a positive number. Exiting.\u0026#39;); end otherwise error(\u0026#39;Unrecognised option. Exiting.\u0026#39;) end end if nargout \u0026gt;=2 err_mse = zeros(MAXITER,1); end if nargout ==3 iter_time = zeros(MAXITER,1); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Make P and Pt functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if isa(A,\u0026#39;float\u0026#39;) P =@(z) A*z; Pt =@(z) A\u0026#39;*z; elseif isobject(A) P =@(z) A*z; Pt =@(z) A\u0026#39;*z; elseif isa(A,\u0026#39;function_handle\u0026#39;) try if isa(Pt,\u0026#39;function_handle\u0026#39;); P=A; else error(\u0026#39;If P is a function handle, Pt also needs to be a function handle. Exiting.\u0026#39;); end catch error(\u0026#39;If P is a function handle, Pt needs to be specified. Exiting.\u0026#39;); end else error(\u0026#39;P is of unsupported type. Use matrix, function_handle or object. Exiting.\u0026#39;); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Do we start from zero or not? %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if initial_given ==1; if length(find(s_initial)) \u0026gt; M display(\u0026#39;Initial vector has more than M non-zero elements. Keeping only M largest.\u0026#39;) end s = s_initial; [ssort sortind] = sort(abs(s),\u0026#39;descend\u0026#39;); s(sortind(M+1:end)) = 0; Ps = P(s); Residual = x-Ps; oldERR = Residual\u0026#39;*Residual/n; else s_initial = zeros(m,1); Residual = x; s = s_initial; Ps = zeros(n,1); oldERR = sigsize; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Random Check to see if dictionary norm is below 1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% x_test=randn(m,1); x_test=x_test/norm(x_test); nP=norm(P(x_test)); if abs(MU*nP)\u0026gt;1; display(\u0026#39;WARNING! Algorithm likely to become unstable.\u0026#39;) display(\u0026#39;Use smaller step-size or || P ||_2 \u0026lt; 1.\u0026#39;) end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Main algorithm %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if verbose display(\u0026#39;Main iterations...\u0026#39;) end tic t=0; done = 0; iter=1; while ~done if MU == 0 %Calculate optimal step size and do line search olds = s; oldPs = Ps; IND = s~=0; d = Pt(Residual); % If the current vector is zero, we take the largest elements in d if sum(IND)==0 [dsort sortdind] = sort(abs(d),\u0026#39;descend\u0026#39;); IND(sortdind(1:M)) = 1; end id = (IND.*d); Pd = P(id); mu = id\u0026#39;*id/(Pd\u0026#39;*Pd); s = olds + mu * d; [ssort sortind] = sort(abs(s),\u0026#39;descend\u0026#39;); s(sortind(M+1:end)) = 0; Ps = P(s); % Calculate step-size requirement omega = (norm(s-olds)/norm(Ps-oldPs))^2; % As long as the support changes and mu \u0026gt; omega, we decrease mu while mu \u0026gt; (0.99)*omega \u0026amp;\u0026amp; sum(xor(IND,s~=0))~=0 \u0026amp;\u0026amp; sum(IND)~=0 % display([\u0026#39;decreasing mu\u0026#39;]) % We use a simple line search, halving mu in each step mu = mu/2; s = olds + mu * d; [ssort sortind] = sort(abs(s),\u0026#39;descend\u0026#39;); s(sortind(M+1:end)) = 0; Ps = P(s); % Calculate step-size requirement omega = (norm(s-olds)/norm(Ps-oldPs))^2; end else % Use fixed step size s = s + MU * Pt(Residual); [ssort sortind] = sort(abs(s),\u0026#39;descend\u0026#39;); s(sortind(M+1:end)) = 0; Ps = P(s); end Residual = x-Ps; ERR=Residual\u0026#39;*Residual/n; if comp_err err_mse(iter)=ERR; end if comp_time iter_time(iter)=toc; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Are we done yet? %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if comp_err \u0026amp;\u0026amp; iter \u0026gt;=2 if ((err_mse(iter-1)-err_mse(iter))/sigsize\u0026lt;STOPTOL); if verbose display([\u0026#39;Stopping. Approximation error changed less than \u0026#39; num2str(STOPTOL)]) end done = 1; elseif verbose \u0026amp;\u0026amp; toc-t\u0026gt;10 display(sprintf(\u0026#39;Iteration %i. --- %i mse change\u0026#39;,iter ,(err_mse(iter-1)-err_mse(iter))/sigsize)) t=toc; end else if ((oldERR - ERR)/sigsize \u0026lt; STOPTOL) \u0026amp;\u0026amp; iter \u0026gt;=2; if verbose display([\u0026#39;Stopping. Approximation error changed less than \u0026#39; num2str(STOPTOL)]) end done = 1; elseif verbose \u0026amp;\u0026amp; toc-t\u0026gt;10 display(sprintf(\u0026#39;Iteration %i. --- %i mse change\u0026#39;,iter ,(oldERR - ERR)/sigsize)) t=toc; end end % Also stop if residual gets too small or maxIter reached if comp_err if err_mse(iter)\u0026lt;1e-16 display(\u0026#39;Stopping. Exact signal representation found!\u0026#39;) done=1; end elseif iter\u0026gt;1 if ERR\u0026lt;1e-16 display(\u0026#39;Stopping. Exact signal representation found!\u0026#39;) done=1; end end if iter \u0026gt;= MAXITER display(\u0026#39;Stopping. Maximum number of iterations reached!\u0026#39;) done = 1; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % If not done, take another round %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if ~done iter=iter+1; oldERR=ERR; end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Only return as many elements as iterations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if nargout \u0026gt;=2 err_mse = err_mse(1:iter); end if nargout ==3 iter_time = iter_time(1:iter); end if verbose display(\u0026#39;Done\u0026#39;) end ","date":"2026-02-10T00:00:00Z","permalink":"https://blog.detail.eu.org/p/146a5f/","title":"IHT"},{"content":"基本原理 Majorization-Minimization优化框架在各类算法中很常见。在目标函数$J(x)$难优化时，可以寻找一个容易优化的目标函数$G(x)$，当$G(x)$满足一定条件时，$G(x)$的最优解能无限逼近$J(x)$的最优解。 $G(x)$应该满足的条件 容易优化 $G_k(x)\\ge J(x)$ $G_k(x_k)=J(x_k)$ 优化过程 考虑一个优化问题，$\\mathscr X$为闭凸集、$J(\\mathbf x)$连续\n$$ \\min_\\mathbf x J(\\mathbf x)\\quad \\text{s.t.} \\quad x \\in \\mathscr X. $$\n1 2 3 4 5 6 7 8 9 10 \\begin{algorithm} \\caption{Majorization Minimization} \\begin{algorithmic} \\STATE 寻找一个可行点$x^0 \\in \\mathscr X，k \\gets 0$ \\REPEAT \\STATE $\\mathbf x^{k+1}=\\arg\\min_{x\\in \\mathscr X} G_k(\\mathbf{x})$ \\STATE $k\\gets k+1$ \\UNTIL{满足一些条件} \\end{algorithmic} \\end{algorithm} ","date":"2026-01-23T00:00:00Z","image":"https://blog.detail.eu.org/p/c62fc1/b5b3d6bcffab9a324c9342ea9be2ec59_hu_e48199f4f58a3fae.png","permalink":"https://blog.detail.eu.org/p/c62fc1/","title":"MM 优化框架"},{"content":"流程图 配准 强度图像金字塔 使用主辅图像的幅值构建影像金字塔，自顶向下逐层对图像进行匹配。1使得每轮匹配的搜索范围都相对较小，快速接近对应位置。代码中采用4层金字塔，上一层图像为下一层图像在距离向和方位向分别进行2倍下采样得到。匹配窗口为$21\\times 21$、每层最大偏移为$15\\times 15$、控制点数量为64。图像偏移量为控制点偏移量的均值。控制点匹配采用归一化相关匹配，使用相关性最高的偏移量为该控制点的偏移量。2\n$$ R\\left( x,y \\right) =\\frac{\\sum_{x^\\prime ,y^\\prime }{M\\left( x^\\prime ,y^\\prime \\right) \\cdot S\\left( x+x^\\prime ,y+y^\\prime \\right)}}{\\sqrt{\\sum_{x^\\prime ,y^\\prime }{M\\left( x^\\prime ,y^\\prime \\right) ^2}\\cdot \\sum_{x^\\prime ,y^\\prime}{S\\left( x+x^\\prime ,y+y^\\prime \\right) ^2}}} $$\n最大频谱法精匹配 使用最大频谱法对主辅图像进行匹配，使得主辅图像对齐到亚像元级别以满足后续处理需求。代码中采用的匹配窗口为$15\\times 15$、最大偏移为$3\\times 3$、控制点数量为64，计算前在图像距离向和方位向上分别进行5倍上采样。控制点偏移量使用最大频谱法得到的信噪比最大的偏移量。3\n$$ u_{int}=u_m\\cdot u_{s}^{*}=a_ma_s\\cdot e^{j\\left( \\varphi _m-\\varphi _s \\right)} $$$$ SNR=\\frac{f_{max}}{\\sum{f_{i,j}}-f_{max}} $$图像偏移量使用控制点去除奇异值点和信噪比较低的点后，使用三阶多项式拟合。3\n$$ \\left\\{ \\begin{array}{l} \\Delta x=a_0+a_1\\cdot x+a_2\\cdot y+a_3\\cdot xy+a_4\\cdot x^2+a_5\\cdot y^2+a_6\\cdot x^2y+a_7\\cdot xy^2+a_8\\cdot x^3+a_9\\cdot y^3\\\\ \\Delta y=b_0+b_1\\cdot x+b_2\\cdot y+b_3\\cdot xy+b_4\\cdot x^2+b_5\\cdot y^2+b_6\\cdot x^2y+b_7\\cdot xy^2+b_8\\cdot x^3+b_9\\cdot y^3\\\\ \\end{array} \\right. $$插值 应用偏移量到辅图像上采用复数双线性插值\n$$ f\\left( x,y \\right) =f\\left( Q_{11} \\right) w_{11}+f\\left( Q_{21} \\right) w_{21}+f\\left( Q_{12} \\right) w_{12}+f\\left( Q_{22} \\right) w_{22} $$$$ \\left\\{ \\begin{align*} w_{11} \u0026= \\frac{(x_2-x)(y_2-y)}{(x_2-x_1)(y_2-y_1)}\\\\ w_{21} \u0026= \\frac{(x-x_1)(y_2-y)}{(x_2-x_1)(y_2-y_1)}\\\\ w_{12} \u0026= \\frac{(x_2-x)(y-y_1)}{(x_2-x_1)(y_2-y_1)}\\\\ w_{22} \u0026= \\frac{(x-x_1)(y-y_1)}{(x_2-x_1)(y_2-y_1)} \\end{align*} \\right. $$干涉 干涉图由主辅图像共轭相乘得到，相干图由干涉图用$9\\times 9$窗口均值滤波并归一化得到。3\n$$ \\begin{align*} u_{int}\u0026=u_m\\cdot u_{s}^{*}=a_ma_s\\cdot e^{j\\left( \\varphi _m-\\varphi _s \\right)}=a_{int}e^{j\\varphi} \\\\ \\varphi \u0026=\\varphi _\\text{flat}+\\varphi _\\text{topo}+\\varphi _\\text{atm}+\\varphi _\\text{noise} \\end{align*} $$$$ \\gamma =\\frac{|E\\left[ M\\cdot S^* \\right] |}{\\sqrt{E\\left[\\mid M\\mid ^2 \\right] E\\left[\\mid S\\mid ^2 \\right]}} $$去平地效应 斜距$R$和入射角$\\theta$在SML文件中得到，使用线性插值对斜距$R$进行插值。$\\Delta 𝑅$为当前像元与第一个像元斜距之差。 3 $R_0$为第一个像元的斜距，$R_{eq}$为干涉相位为0时对应的斜距。\n$$ \\varphi _\\text{flat}=-\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}\\Delta \\text{R}}{Rtan\\left( \\theta \\right)}+\\varphi _\\text{const} $$$$ \\varphi_\\text{const}=-\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}(R_{0}-R_{\\text{eq}})}{R_{0}tan\\left( \\theta \\right)} $$去平地效应后的干涉图相位:\n$$ \\begin{align*} \\varphi _1\u0026 =\\varphi +\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}\\Delta \\text{R}}{Rtan\\left( \\theta \\right)}\\\\ \u0026 =\\varphi _\\text{topo}+\\varphi _\\text{atm}+\\varphi _\\text{const}+\\varphi _\\text{noise}\\\\ \\end{align*} $$去DEM相位 使用轨道数据将DEM数据逐点从WGS84坐标系转换到雷达距离-方位坐标系，使用线性插值将得到的数据插值到整个雷达距离-方位坐标系。通过高程相位的公式得到 3\n$$ \\varphi _\\text{dem}=-\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}\\text{H}_\\text{dem}}{R\\sin \\left( \\theta \\right)} $$去DEM相位后的干涉图相位:\n$$ \\begin{align*} \\varphi _2\u0026 =\\varphi _1-\\varphi _\\text{dem}\\\\ \u0026 =\\varphi _\\text{topo}-\\varphi _\\text{dem}+\\varphi _\\text{atm}+\\varphi _\\text{const}+\\varphi _\\text{noise}\\\\ \u0026 =-\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}\\Delta H}{R\\sin \\left( \\theta \\right)}+\\varphi _\\text{atm}+\\varphi _\\text{const}+\\varphi _\\text{noise} \\quad\\quad (\\Delta H = H - H_\\text{dem})\\\\ \\end{align*} $$滤波 使用Goldstein滤波，将干涉图分成相互重叠的小块。对每个小块做傅里叶变换，得到其频谱$Z(u,v)$ , 用其对干涉图进行处理。$S[\\cdot]$ 为平滑算子，使用$3\\times 3$的均值滤波。Goldstein滤波窗口大小为$32\\times 32$ , 步长为8 。45\n$$ H(u,v) = S[ \\lvert Z(u,v)\\rvert ]^{1-\\bar \\gamma}\\cdot Z(u,v) $$忽略残余噪声，滤波后干涉图相位:\n$$ \\varphi_3 = =-\\frac{4\\pi}{\\lambda}\\cdot \\frac{B_{\\bot}\\Delta H}{R\\sin \\left( \\theta \\right)}+\\varphi _\\text{atm}+\\varphi _\\text{const}\\\\ $$大气相位校正 经过去平地效应和去DEM相位后，相位还剩余高频的相对高程相位与低频的大气相位和固定相位。对相位使用高通滤波器去除大气相位和固定相位。6 假设$E[\\Delta H]=0$\n$$ \\varphi _3=k\\Delta H+\\varphi _\\text{atm}+\\varphi _\\text{const} \\quad\\quad ( k=-\\frac{4\\pi B_{\\bot}}{\\lambda Rsin(\\theta)}) $$去大气相位后的干涉图相位:\n$$ \\varphi _4=HPF( \\varphi _3) =k\\Delta H $$ 靳国旺. InSAR获取高精度DEM关键处理技术研究[D]. 解放军信息工程大学, 2007.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOpenCV计算机视觉库. 模板匹配[EB/OL], (2025-7-3). https://docs.opencv.ac.cn/4.12.0/de/da9/tutorial_template_matching.html\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n杨红磊, 彭军还, 康志忠. InSAR技术原理及实践[M]. 北京：科学出版社, 2021.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nBARAN I, STEWART M P, KAMPES B M, 等. A modification to the goldstein radar interferogram filter[J]. IEEE Transactions on Geoscience and Remote Sensing, 2003, 41(9): 2114-2118.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNome2s. InSARFilter[CP/OL], (2025-10-21). https://github.com/Nome2s/InSARFilter.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nFORNARO G, LOMBARDINI F, SERAFINO F. Three-dimensional multipass SAR focusing: experiments with long-term spaceborne data[J]. IEEE Transactions on Geoscience and Remote Sensing, 2005, 43(4): 702-714.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-11-21T00:00:00Z","image":"https://blog.detail.eu.org/p/197481/intf_hu_c25d57c8e3dafe05.png","permalink":"https://blog.detail.eu.org/p/197481/","title":"成像预处理"},{"content":"利用信号的稀疏性作为先验知识，完成对信号的压缩。\n基本公式 $x$ 为信号 $s$ 为稀疏系数，$x=\\Psi s$ $y$ 为观测值，$y = \\Phi\\Psi s = Rs$ $\\Phi$ 为观测矩阵 $\\Psi$ 为稀疏矩阵 如何重建 $L_0$最小范数(理论解) $$ \\min_s \\Vert s\\Vert_0\\quad s.t. \\quad y=Rs $$$L_1$最小范数 求解$L_0$最小范数是NP-hard问题，但如果信号足够稀疏（$M \\gg O(Klog(N/K))$），可以使用$L_1$最小范数得到相同结果。\n$$ \\min_s \\Vert s\\Vert_1\\quad s.t. \\quad y=Rs $$存在噪声的情况下 没有K的先验知识 $$ \\hat s = \\arg\\min_s{\\{\\Vert y-Rs\\Vert_2^2+\\lambda_K\\Vert s\\Vert_1\\}} $$$\\lambda_K$为拉格朗日乘子，取决于采样点数$N$和噪声水平$\\sigma_\\varepsilon$\n有K的先验知识 $$ \\hat s= \\min_s \\Vert s\\Vert_1\\quad s.t. \\quad \\Vert y-Rs\\Vert_2 \\lt \\sigma_\\varepsilon $$性质 非相关性 通过最大化每次测量的信息效率，分散信号特征到所有采样点中。\n$$ \\mu(\\mathrm{\\bf{R}})=\\mu(\\bf{\\Phi},\\bf{\\Psi})=\\sqrt{N}\\operatorname*{max}_{1\\leq k,j\\leq N}\\frac{\\vert\\langle\\varphi_{k},\\psi_{j}\\rangle\\vert}{\\Vert\\varphi_{k}\\Vert_2\\Vert\\psi_{j}\\Vert_2} $$典型测量基$\\Phi$ 和稀疏基$\\Psi$组合 1. 傅里叶矩阵+单位矩阵：当信号自身具有稀疏性时 2. 噪声基+小波基 3. 随机矩阵+任意固定基\n限制等距条件(RIP) 保证测量矩阵对稀疏信号的几何结构保持稳定\n$$ \\left(1-\\delta_{s}\\right)\\Vert \\nu\\Vert_{2}^{2}\\ \\leq\\ \\Vert\\mathbf{R}\\,\\nu\\Vert_{2}^{2}\\ \\leq\\ \\left(1+\\delta_{s}\\right)\\Vert\\nu\\Vert_{2}^{2} $$典型重建方法 基于松弛理论 凸优化 凸优化算法利用$L_1$范数最小化来求解$L_0$问题等效的凸优化问题问题，问题如下\n$$ \\hat s = \\arg\\min_s{\\lVert s\\rVert_1} \\qquad \\text{s.t.} \\quad \\lVert Rs-y\\rVert_2\\le\\varepsilon. $$该类算法可以分为内点法（Interior Point Method, IPM）和一阶算法。 内点法的典型算法有基追踪（BP）、$L_1$正则化最小二乘（$L_1$-LS）、$L_1$-magic等。它们属于重构精度较高的高阶算法，但是它们对高阶问题的求解效率很低。 一阶算法的代表有梯度投影（GPSR）、软阈值迭代（IST, TwIST, FIST）、Bregman、不动点延拓（FPC, FPC-AC）、基于Nesterov理论的算法（NESTA）、一阶锥求解器（TFOCS）、交替方向法（ADM/ADMM）等。这类方法对高维问题的求解非常有效。 $L_1$范数最小化算法可以用来重构\u0026lt;u\u0026gt;稀疏性较差的信号\u0026lt;/u\u0026gt;，对加性噪声具有良好的鲁棒性。\n非凸优化 非凸优化与之类似，主要的研究方向是基于迭代重加权原理和基于$L_q(0\u0026lt;q\u0026lt;1)$正则化理论。 基于迭代重加权原理的算法由优化最小法（Majorization Minimization）框架推导而来。基本思想是利用与未知信号相关的加权矩阵求解一个优化问题序列。典型的算法有迭代重加权最小二乘（IRLS）和FOCUSS等。 基于$L_q(0\u0026lt;q\u0026lt;1)$正则化理论的算法所要求解的问题如下\n$$ \\hat s = \\arg\\min_s \\{{\\lVert Rs-y\\rVert_2}+\\lambda\\lVert s\\rVert^q_q\\}. $$典型的算法有$L_{1/2}$阈值迭代算法和$L_{2/3}$阈值迭代算法。 相比于凸优化算法，非凸优化算法通常对稀疏信号的\u0026lt;u\u0026gt;重构精度更高\u0026lt;/u\u0026gt;，然而若参数选择不合适\u0026lt;u\u0026gt;极易收敛到局部极小值\u0026lt;/u\u0026gt;，导致信号重构结果出现偏差。\n基于贪婪追踪 贪婪追踪算法是一类搜索算法，此类算法主要包含两个步骤，即支集选择和系数更新。现有各种贪婪追踪算法主要由匹配追踪（MP）和正交匹配追踪（OMP）发展而来。典型的算法有OMP、IHT等，此类算法优势是\u0026lt;u\u0026gt;在稀疏性较强时，能迅速重建所需信号。当信号稀疏性较弱或测量存在噪声时，运算效率低、重建结果与真实值之间会存在很大偏差\u0026lt;/u\u0026gt;\n基于贝叶斯 贝叶斯重构算法是通过将信号稀疏性与先验信息关联，从概率论角度求解稀疏信号的一类算法。典型的有基于稀疏性贝叶斯学习和基于图模型的信息传递算法。\n","date":"2025-11-19T00:00:00Z","image":"https://blog.detail.eu.org/p/007a07/ae2557342ff4f654eac932f3bda28363_hu_e847a8df75ac1e57.png","permalink":"https://blog.detail.eu.org/p/007a07/","title":"压缩感知"},{"content":"联通校园网自动登陆的方法 获取自动登陆脚本 新建一个文件夹用于存放脚本C:/AutoJobs 新建一个文件，并命名为autoAuth.pyw 将附录代码1（完成自动登陆的python代码）复制到文件 如果你未修改过密码可以不执行后两步操作（即账号为02505+密码，在下面的配置任务计划时需要额外操作） 将代码DEF_UserName设置为你的账号（字符串类型，如*\u0026ldquo;02505123456\u0026rdquo;*） 将代码DEF_PassWord设置为你的密码（字符串类型，如*\u0026ldquo;123456\u0026rdquo;*） 安装python及环境配置 从Python官网下载python安装包（https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe） 安装python，需要勾选Add Python.exe to PATH，然后选择Install Now安装 按Win+R启动的运行窗口，输入cmd并回车启动命令行窗口 输入pip install beautifulsoup4并回车安装bs4库（请保证网络畅通） 配置Windows任务计划 打开计算机管理并创建任务计划 在Windows徽标处右键 在右键菜单中选择计算机管理 在计算机管理(本地)-\u0026gt;系统工具-\u0026gt;任务计划程序-\u0026gt;任务计划程序库处创建新文件夹 AutoJobs 右键新建任务 选择常规选项卡，填入名称 autoAuth，描述 Nuaa联通校园网ChinaUnicom自动登陆(选填)。安全选项选择不管用户是否登陆都要运行并勾选不存储密码 选择触发器选项卡，点击新建。在打开的窗口中开始任务选择发生事件时，设置选择自定义 点击新建事件筛选器，在打开的窗口中选择XML选项卡并勾选手动编辑查询，在框中填入附录代码2（自定义事件筛选器配置） 选择操作选项卡，点击新建。在打开的窗口中操作选择启动程序。在设置中，程序或脚本填入pythonw程序路径（C:/Users/[YourUserName]/AppData/Local/Programs/Python/Python311/pythonw.exe，[YourUserName]替换为你的用户名，python311替换为你的python版本），添加参数填入*-u autoAuth.pyw 密码[未配置默认密码时]*，起始于填入autoAuth脚本所在目录（C:/AutoJobs） 选择条件选项卡，在电源中取消勾选只有计算机使用交流电源时才启动此任务 故障排查 错误代码（任务计划管理器的上次运行结果） 0x1: 未配置用户名，即未完成1.4和1.5的操作 0x2 : 未连接到目标WIFI，即触发器误触发，代码2出错 0x3 : 未获取到IPv4地址，即网络故障 0x4 : 获取paramStr失败，即联通的登陆方式发生变换，代码1不再适用 0x5 : 认证失败，即登陆服务器返回不正常 0x6 : 认证失败，凭据错误 附录 代码1 完成自动登陆的python代码\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 import subprocess import requests import os import time import sys from bs4 import BeautifulSoup as bs from time import sleep # 错误代码 # 0x01 : 未配置用户名 # 0x02 : 未连接到目标WIFI # 0x03 : 未获取到IPv4地址 # 0x04 : 获取paramStr失败 # 0x05 : 认证失败 # 0x06 : 认证失败，凭据错误 # 默认用户名和密码 # 如果环境变量中设置了ChinaUnicom_WIFI_USERNAME和ChinaUnicom_WIFI_PASSWORD，则使用这些值 # 如果提供未修改的密码，则自动生成用户名 # 如果环境变量中没有设置，则使用默认值 DEF_UserName = None DEF_PassWord = None if DEF_UserName is None or DEF_PassWord is None: USERNAME = os.getenv(\u0026#34;ChinaUnicom_WIFI_USERNAME\u0026#34;) PASSWORD = os.getenv(\u0026#34;ChinaUnicom_WIFI_PASSWORD\u0026#34;) else: USERNAME = os.getenv(\u0026#34;ChinaUnicom_WIFI_USERNAME\u0026#34;, DEF_UserName) PASSWORD = os.getenv(\u0026#34;ChinaUnicom_WIFI_PASSWORD\u0026#34;, DEF_PassWord) if USERNAME is None or PASSWORD is None: USERID = sys.argv[1] if len(sys.argv) \u0026gt; 1 else None if USERID is not None: USERNAME = \u0026#34;02505\u0026#34; + USERID PASSWORD = USERID # 日志文件 LOGFILE = \u0026#34;auth.log\u0026#34; LOG_LIMIT = 300 # 判断日志行数并删除旧日志 def clean_log(): if os.path.exists(LOGFILE): with open(LOGFILE, \u0026#34;r\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as log_file: lines = log_file.readlines() if len(lines) \u0026gt; LOG_LIMIT: with open(LOGFILE, \u0026#34;w\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as log_file: log_file.writelines(lines[-LOG_LIMIT:]) # 日志记录函数 def logger(message): print(message) with open(LOGFILE, \u0026#34;a\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as log_file: log_file.write(f\u0026#34;{time.strftime(\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;)} - {message}\\n\u0026#34;) def end_process(message, error_code=0): logger(message) exit(error_code) clean_log() logger(\u0026#34;程序启动，开始自动认证流程。\u0026#34;) # 检查用户名和密码是否配置 if USERNAME is None or PASSWORD is None: end_process(\u0026#34;未配置用户名或密码，程序退出。\u0026#34;, 0x01) # 测试网络连接 CHCP_CMD = \u0026#34;chcp 65001\u0026#34; PING_TEST_URL = \u0026#34;www.bing.com\u0026#34; PING_TEST_CMD = f\u0026#34;ping -n 1 {PING_TEST_URL}\u0026#34; PING_TEST_CMD = f\u0026#34;{CHCP_CMD} \u0026amp;\u0026amp; {PING_TEST_CMD}\u0026#34; def ping_test(): try: result = subprocess.run(PING_TEST_CMD, shell=True, capture_output=True, text=True) if \u0026#34;TTL=\u0026#34; in result.stdout: return True else: return False except Exception as e: logger(f\u0026#34;网络连接测试失败: {e}\u0026#34;) return False # ------ 检查网络连接 ------ if ping_test(): end_process(\u0026#34;网络连接正常，无需认证。\u0026#34;, 0) # 获取连接的WIFI名称 GET_WIFI_CMD = \u0026#34;netsh wlan show interfaces\u0026#34; GET_WIFI_CMD = f\u0026#34;{CHCP_CMD} \u0026amp;\u0026amp; {GET_WIFI_CMD}\u0026#34; ATTR_WIFI_KEYWORD = \u0026#34;SSID\u0026#34; def get_current_wifi(): try: result = subprocess.run(GET_WIFI_CMD, shell=True, capture_output=True, text=True) if result.returncode != 0: return None output = result.stdout for line in output.splitlines(): if ATTR_WIFI_KEYWORD in line: return line.split(\u0026#34;:\u0026#34;)[1].strip() except Exception as e: logger(f\u0026#34;无法获取WiFi名称: {e}\u0026#34;) return None # 检查当前连接的WIFI是否为目标WIFI DST_WIFI = \u0026#34;ChinaUnicom\u0026#34; RETRY_TIME = 3 RETRY_COUNT = 3 def check_wifi(): current_wifi = get_current_wifi() if current_wifi != DST_WIFI: logger(f\u0026#34;当前WiFi SSID: {current_wifi}. 等待连接到 {DST_WIFI}...\u0026#34;) return False logger(f\u0026#34;已连接到 {DST_WIFI}.\u0026#34;) return True # ------ 等待连接到目标WIFI ------ while not check_wifi(): sleep(RETRY_TIME) RETRY_COUNT -= 1 if RETRY_COUNT \u0026lt;= 0: end_process(f\u0026#34;未连接到目标WIFI {DST_WIFI}，请检查网络设置。\u0026#34;, 0x02) # 获取当前IPv4地址 GET_IP_CMD = \u0026#34;ipconfig\u0026#34; GET_IP_CMD = f\u0026#34;{CHCP_CMD} \u0026amp;\u0026amp; {GET_IP_CMD}\u0026#34; DST_ADAPTERS = [\u0026#34;无线局域网适配器 WLAN\u0026#34;, \u0026#34;Wireless LAN adapter WLAN\u0026#34;] END_KEYWORD = \u0026#34;适配器\u0026#34; ATTR_IPV4_KEYWORD = [\u0026#34;IPv4 地址\u0026#34;, \u0026#34;IPv4 Address\u0026#34;] RETRY_TIME2 = 3 RETRY_COUNT2 = 3 def get_wifi_ip(): try: result = subprocess.run(GET_IP_CMD, shell=True, capture_output=True, text=True) if result.returncode != 0: return None output = result.stdout lines = output.splitlines() wlan_section = False for line in lines: if any(adapter in line for adapter in DST_ADAPTERS): wlan_section = True continue elif END_KEYWORD in line and wlan_section: break elif wlan_section and any(keyword in line for keyword in ATTR_IPV4_KEYWORD): ip = line.split(\u0026#34;:\u0026#34;)[-1].strip() return ip except Exception as e: logger(f\u0026#34;无法获取IPv4地址: {e}\u0026#34;) return None # ------ 获取当前IPv4地址 ------ CRT_IP = get_wifi_ip() while not CRT_IP: logger(\u0026#34;无法获取当前IPv4地址, 正在重试...\u0026#34;) sleep(RETRY_TIME2) CRT_IP = get_wifi_ip() RETRY_COUNT2 -= 1 if RETRY_COUNT2 \u0026lt;= 0: end_process(\u0026#34;未获取到IPv4地址, 请检查网络设置。\u0026#34;, 0x03) logger(f\u0026#34;当前IPv4地址: {CRT_IP}\u0026#34;) # ------ 再次检查网络连接 ------ if ping_test(): end_process(\u0026#34;网络连接正常，无需认证。\u0026#34;, 0) # 获取连接所需的参数 BASE_URL = \u0026#34;http://58.240.51.118\u0026#34; PAGE_URL = BASE_URL + \u0026#34;/?wlanuserip=\u0026#34; + str(CRT_IP) + \u0026#34;\u0026amp;basname=\u0026amp;ssid=school\u0026#34; def get_paramStr(): # 主页面 page_info = requests.request(\u0026#34;GET\u0026#34;, PAGE_URL) if page_info.status_code != 200: return None page_context = page_info.text soup = bs(page_context, \u0026#34;html.parser\u0026#34;) main_frame = soup.find(\u0026#34;frame\u0026#34;, {\u0026#34;name\u0026#34;: \u0026#34;mainFrame\u0026#34;}) if not main_frame: return None frame_src = main_frame[\u0026#34;src\u0026#34;] # type: ignore # 子页面 frame_url = BASE_URL + str(frame_src) page_info2 = requests.request(\u0026#34;GET\u0026#34;, frame_url) if page_info2.status_code != 200: return None page_context2 = page_info2.text soup2 = bs(page_context2, \u0026#34;html.parser\u0026#34;) paramStr_input = soup2.find(\u0026#34;input\u0026#34;, {\u0026#34;name\u0026#34;: \u0026#34;paramStr\u0026#34;}) paramStr = paramStr_input[\u0026#34;value\u0026#34;] # type: ignore return paramStr # 自动认证 PROVINCE = \u0026#34;wlan.js.chinaunicom.cn\u0026#34; GDYH = \u0026#34;prov\u0026#34; URL = BASE_URL + \u0026#34;/authServlet\u0026#34; SUCCESS_KEYWORD = \u0026#34;本次上网时长：\u0026#34; # ------- 自动认证 -------- try: paramStr = get_paramStr() if paramStr is None: raise ValueError(\u0026#34;无法从页面获取paramStr。\u0026#34;) except Exception as e: end_process(f\u0026#34;获取paramStr失败: {e}\u0026#34;, 0x04) FORM_DATA = { \u0026#34;province\u0026#34;: PROVINCE, \u0026#34;paramStr\u0026#34;: paramStr, \u0026#34;gdyh\u0026#34;: GDYH, \u0026#34;shortname\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;UserName\u0026#34;: USERNAME, \u0026#34;PassWord\u0026#34;: PASSWORD, } response = requests.request(\u0026#34;POST\u0026#34;, URL, data=FORM_DATA) if response.status_code == 200: logger(\u0026#34;收到响应，正在检查认证结果...\u0026#34;) if not SUCCESS_KEYWORD in response.text: end_process(\u0026#34;认证失败，请检查您的凭据。\u0026#34;, 0x06) else: end_process(f\u0026#34;认证失败，状态码: {response.status_code}\u0026#34;, 0x05) logger(f\u0026#34;认证成功，已完成自动认证。用户名: {USERNAME}, IPv4地址: {CRT_IP}\u0026#34;) # 防止连续多次触发 sleep(10) logger(\u0026#34;程序结束，等待下次触发。\u0026#34;) 代码2 自定义事件筛选器配置（二选一，推荐使用ActiveHttpProbeFailedHotspotDetected触发）\n连接WIFI(ChinaUnicom)时触发（不推荐） 1 2 3 4 5 6 7 8 \u0026lt;QueryList\u0026gt; \u0026lt;Query Id=\u0026#34;0\u0026#34; Path=\u0026#34;Microsoft-Windows-WLAN-AutoConfig/Operational\u0026#34;\u0026gt; \u0026lt;Select Path=\u0026#34;Microsoft-Windows-WLAN-AutoConfig/Operational\u0026#34;\u0026gt; *[System[(EventID=11005)]] and *[EventData[Data[@Name=\u0026#39;SSID\u0026#39;]=\u0026#39;ChinaUnicom\u0026#39;]] \u0026lt;/Select\u0026gt; \u0026lt;/Query\u0026gt; \u0026lt;/QueryList\u0026gt; 检测到需要网页认证的热点(ActiveHttpProbeFailedHotspotDetected)时触发（推荐） 1 2 3 4 5 6 7 8 \u0026lt;QueryList\u0026gt; \u0026lt;Query Id=\u0026#34;0\u0026#34; Path=\u0026#34;Microsoft-Windows-NCSI/Operational\u0026#34;\u0026gt; \u0026lt;Select Path=\u0026#34;Microsoft-Windows-NCSI/Operational\u0026#34;\u0026gt; *[System[(EventID=4042)]] and *[EventData[Data[@Name=\u0026#39;CapabilityChangeReason\u0026#39;]=7]] \u0026lt;/Select\u0026gt; \u0026lt;/Query\u0026gt; \u0026lt;/QueryList\u0026gt; ActiveHttpProbeFailedHotspotDetected HTTP 探测无法穿越热点或强制验证门户。 This is typically determined when an HTTP response 200 is received, but the response payload doesn’t contain the text file connecttest.txt. 或者，可能会收到非 200 HTTP 状态代码（如 302 或 304）。 处理无法建立无线连接的问题时，通常会观察到此状态代码。 通过数据包捕获进行验证。 用户可能需要对热点进行身份验证，或者可能需要修改热点配置。\n","date":"2025-08-31T00:00:00Z","permalink":"https://blog.detail.eu.org/p/03300d/","title":"联通校园网自动登陆"},{"content":"SAR几何 参考方向 $x$: azimuth 传感器的飞行方向，也称为沿轨道方向 $r$: range 天线的视线（LOS）方向，也称为倾斜范围 $\\theta$: elevation angle 高程角，即垂直于方位角平面。高程$s$ 分辨率 飞行方向 $$ \\rho_x = \\frac{\\lambda r}{2\\Delta x} $$视线方向 $$ \\rho_r = \\frac{c}{2W} $$高程方向（TomoSAR） $$ \\rho_s = = \\frac{\\lambda r}{2\\Delta b} $$SAR原理 实际孔径天线 阵列天线长$L$，均匀加权。工作波长$\\lambda$ ，目标偏离视轴角度$\\theta$，$x$为接收点偏离相位基准点的位置。回波信号单程相位差：\n$$ \\varphi(x) = \\frac{2\\pi}{\\lambda}xsin\\theta $$天线方向图函数：\n$$ F(\\theta)=\\frac{1}{L}\\int^{L/2}_{-L/2}e^{j\\varphi(x)}dx=\\frac{sin(\\frac{\\pi}{\\lambda}Lsin\\theta)}{\\frac{\\pi}{\\lambda}Lsin\\theta} $$收发双程半功率点分辨率：\n$$ \\delta r_c|_{3dB}(双程)\\approx 0.64\\frac{\\lambda r}{L} $$离散阵元阵列天线方向图函数：\n$$ F(\\theta)=\\frac{sin[\\frac{\\pi Nd}{\\lambda}sin\\theta]}{Nsin(\\frac{\\pi d}{\\lambda}sin\\theta)} $$非聚焦 双程相位差：\n$$ \\varphi(x)=\\frac{4\\pi}{\\lambda}xsin\\theta \\approx \\frac{4\\pi v_pTy}{\\lambda r} $$天线方向图函数：\n$$ F(\\theta)=\\frac{sin\\frac{2\\pi v_pTy}{\\lambda r}}{\\frac{2\\pi v_pTy}{\\lambda r}}\\quad\\quad y=r\\theta $$$\\frac{2}{\\pi}$幅度处定义的瑞利分辨率：\n$$ \\delta\\gamma_\\alpha=\\frac{\\lambda r}{2L}\\quad\\quad L=v_pT $$在非聚焦处理时，阵面上信号的相位差将影响合成孔径天线波束展宽和 副瓣恶化，为此，孔径$L$受到限制：$L_{max}=\\sqrt{\\lambda r}$，这时$\\Delta r \\le \\frac{\\lambda}{8}$。\n非聚焦处理的分辨率 $$ \\delta\\gamma_\\alpha=\\frac{1}{2}\\sqrt{\\lambda r} $$聚焦 聚焦处理时，附加相位项可以在信号处理过程中予以补偿，故此时合成孔径的长度可由实际天线波束宽度所能覆盖的长度$L_e$所决定。 有效阵列长度$L_e$，天线波束宽度$\\theta_B$：\n$$ \\begin{align*} L_e \u0026= r\\cdot \\theta_B\\\\ \\theta_B \u0026=\\frac\\lambda D \\end{align*} $$聚焦处理的分辨率 $$ \\delta\\gamma_\\alpha=\\frac{D}{2} $$","date":"2025-07-15T00:00:00Z","image":"https://blog.detail.eu.org/p/502d75/SAR_hu_d3caf1a5c17edc30.jpg","permalink":"https://blog.detail.eu.org/p/502d75/","title":"SAR几何"},{"content":"背景 1948年，Claude Shannon在《通信的数学理论》中提出了香农定理，指出了在一个有噪信道中，只要信息的传输速率低于信道容量，总可以找到一种编码方式使得当编码序列足够长时信息实现无差错传输。1 香农定理宣告了在噪声环境下可靠通信的可能性，但是也留下了巨大的挑战，因为他并没有给出这种编码的具体实现方式。为此，在今后的长达半世纪的时间里，无数科学家前仆后继，对此开展大量研究，形成了从汉明码到极化码的突破。信道编码的目的是提高信号传输的可靠性。信道编码的基本原理是在信号码元序列中增加监督码元，并利用监督码元去发现或纠正传输中发生的错误。2 在香农定理理论框架的推动下，信道编码的技术经历了多次重要的突破。 早期的信道编码采用代数的编码方式，如汉明码等，引入奇偶校验的概念，通过构造多项式来实现差错控制。后来引入了概率论工具，Wozencraft、Viterbi提出了最大似然译码，使卷积码在通信系统中得到广泛应用。2但由于译码复杂度呈指数级增长，在实际应用中，这一译码方式受限于设备性能。 20世纪90年代，信道编码技术再次迎来重大突破。Claude Berrou提出了Turbo码。通过递归系统卷积码编码器和交织器，配合迭代译码算法，使信道编码效率接近香农极限，并且由于引入了交织器，译码的复杂度仅呈线性增长。2 Turbo码的提出开创了信道编码的革命性时代，成为3G/4G移动通信的核心技术，推动了现代移动通信的发展。Turbo码的强大不言而喻，但是它存在的译码复杂度高、时延大、存在错误平层、短码性能不佳、对硬件资源要求高等问题，难以满足现代5G要求的低时延，高可靠性，使得它在5G网络中不能得到广泛应用。3 5G中应用最广泛的是LDPC码和Polar码。LDPC码又称低密度奇偶校验码，于1963年由Robert Gallager提出，是一种具有稀疏校验矩阵的线性分组纠错码。由于校验矩阵的稀疏性，LDPC码的编码和译码复杂度较低，适合硬件实现，支持并行处理，提高了译码速度和效率，LDPC码在长码上性能极佳，在5G移动通信中被用作数据信道的编码方案。4 Polar码又称极化码，于2008年由Erdal Arikan提出，是一种基于信道极化理论的前向错误更正编码方式。它通过将多个独立信道转化为两类极端信道5，实现高效可靠的编码传输，与前面几种码类相比，极化码首次达到香农极限，在短码上性能优异，在5G移动通信中被用作控制信道的编码方案。4\n基本原理 极化码的核心思想是的信道极化理论，其包括信道合并(Channel Combining)和信道分裂(Channel Splitting)。当合并的信道数量趋于无穷大时，会出现极化现象。这些信道一部分会趋于无噪信道，另一部分会趋于全噪信道。这时，一部分信道是信道容量将接近1的“好信道”，其余信道是信道容量接近0的“坏信道”。于是就可以在“好信道”上发送信息比特，而在“坏信道”上发送休眠比特。6\n信道对称容量 信道对称容量是一种衡量信道速率的度量。平稳无记忆信道容量为输入与输出的平均互信息的最大值，即$C \\triangleq \\operatorname{max}_{p(x)}I(X;Y)$。对于一个二进制输入离散无记忆信道(B-DMC) $W : X \\to Y$,转移概率为$W(y|x)$ 有：67\n$$C = \\operatorname{max}_{p(x_i)}\\sum_i\\sum_j{p(x_i)p(y_j|x_i)}\\operatorname{log}{\\frac{p(y_j|x_i)}{\\sum_i{p(x_i)p(y_j|x_i)}}}$$ 对于离散对称信道，当输入等概率分布时达到信道容量，因此有：\n$$I(W) \\triangleq \\sum_{y \\in Y}\\sum_{x \\in X}{\\frac{1}{2}W(y|x)\\operatorname{log}{\\frac{W(y|x)}{\\frac{1}{2}W(y|0)+\\frac{1}{2}W(y|1)}}}$$信道极化理论 信道合并 信道合并是指将N个独立的B-DMC信道W，递归地组成一个联合信道$W_N:X_N\\to Y_N, (N=2^n)$，这里的N个信道并非真的有N个物理意义上的信道，这些信道在实现上可以以时分复用的形式使用同一信道。68 当$n=0$时，$W_1=W$，信道即为原来的信道。 当$n=1$时，$W_2:X^2\\to Y^2$ ，两个$W_1$信道合并成为$W_2$。 这时我们计算$U\\to X$的转移矩阵： $$ G_2= \\left[ \\begin{matrix} 1\u00260\\\\ 1\u00261 \\end{matrix} \\right] $$ 信道的状态转移矩阵为： $$ \\begin{align*} W_2(y_1,y_2|u_1,u_2)\u0026=W(y_1|x_1)W(y_2|x_2)\\\\ \u0026=W(y_1|u_1\\oplus u_2)W(y_2|u_2)\\\\ \u0026=W^2(y^2_1|u^2_1G_2) \\end{align*} $$ 当$n=2$时，$W_4:X^4\\to Y^4$ ，两个$W_2$信道合并成为$W_4$。 同理可以得出 $$ \\begin{align*} G_4 \u0026= \\left[ \\begin{matrix} G_2\u00260\\\\ 0\u0026G_2 \\end{matrix} \\right] R_4 \\left[ \\begin{matrix} G_2\u00260\\\\ 0\u0026G_2 \\end{matrix} \\right] \\\\ \u0026= \\left[ \\begin{matrix} 1\u00260\u00260\u00260\\\\ 1\u00260\u00261\u00260\\\\ 1\u00261\u00260\u00260\\\\ 1\u00261\u00261\u00261 \\end{matrix} \\right] \\end{align*} $$ 综上，可以得到组合信道$W_N:X_N\\to Y_N$转移概率： $$ W_N(y^N_1|u^N_1)=W^N(y^N_1|u^N_1G_N) $$信道分裂 信道分裂是将组合信道$W_N:X_N\\to Y_N$分裂为N个二进制信道$W^{(i)}_N:X\\to Y^N\\times X^{i-1}$： $$ W^{(i)}_N(y^N_1,u^{i-1}_1|u_i)=\\sum_{u^N_{i+1}\\in X^{N-i}}{\\frac{1}{2^{N-1}}W_N(y^N_1|u^N_1)} $$ 这里的第i个信道是一输入$N+i-1$输出的信道，这些信道的信道容量有差别。极化码正是利用了信道合并和分裂，将N个信道容量相同的信道变成N个信道容量不相同的信道。8\n信道极化 当$W$ 为删除概率为$\\alpha$ 的二进制对称删除信道(BEC)时，其信道容量为： $$ \\begin{align*} C \u0026= \\operatorname{max}_{p(x_i)}\\{I(X;Y)\\}\\\\ \u0026= \\operatorname{max}_{p(x_i)}\\{H(Y)-H(Y|X)\\}\\\\ \u0026=H(\\alpha,\\frac{1-\\alpha}{2},\\frac{1-\\alpha}{2})-H(\\alpha,1-\\alpha)\\\\ \u0026=1-\\alpha \\end{align*} $$ 以$W_2$为例。信道$W_2$分裂为信道$W^1_2:u_1\\to y_1,y_2$ 和$W^2_2:u_2\\to y_1,y_2,\\hat u_1$ 。已知$x_1=u_1\\oplus u_2$，$x_2=u_2$，可得$u_1=x_1\\oplus x_2$，$u_2=x_2$ 对于信道$W^1_2$，当$x_1$或$x_2$被删除时，$u_1$无法被译码。这时： $$ \\begin{align*} P_e(u_1)\u0026=\\alpha(1-\\alpha)+(1-\\alpha)\\alpha+\\alpha^2\\\\ \u0026=2\\alpha-\\alpha^2 \\end{align*} $$ 对于信道$W^2_2$，情况有所不同，$u_2=u_1\\oplus x_1$ 或$u_2=x_2$，其中$u_1$是已知的。只有当$x_1$和$x_2$被删除时，$u_2$无法被译码9，这时： $$ P_e(u_2)=\\alpha^2 $$ 由此，原本的信道被分为了一个“好信道”和一个“坏信道”。然后对这一单元递归操作，构建出“更好的信道”。 取$\\alpha=0.5$ ，对两个信道合并的极化信道来说：$C(W^1_2)=0.25$、$C(W^2_2)=0.75$。当信道增加到四个时，$C(W^1_4)=0.06$、$C(W^2_4)=0.56$、$C(W^3_4)=0.44$、$C(W^4_4)=0.94$。可以看出当N足够大时，好信道上的误码率将足够小，从而实现可靠通信。Erdal Arikan证明当N足够大时，“好信道”占比为信道容量，因此在无限码长的情况下极化码可以达到香农极限。5\n极化码编码和译码 极化码编码 在极化码的编码过程中，需要选择可靠性最高的$k$个信道来传输信息比特，而其余的$N-k$个信道则用于传输冻结比特。这些冻结比特不携带信息且对于接收端已知。根据极化码的基本原理，可以得到极化码的生成矩阵：69 $$ G_N=B_NF^{\\otimes n}\\qquad \\left\\{ \\begin{align*} B_2\u0026=I_2\\\\ B_N\u0026=R_N(I_2\\otimes B_{\\frac{N}{2}})\\\\ F\u0026= \\left[ \\begin{matrix} 1\u00260\\\\ 1\u00261 \\end{matrix} \\right] \\end{align*} \\right. $$极化码译码 极化码译码最基础的方法是串行消除译码，串行消除译码利用似然比，由$\\hat u_1$开始，逐个译码。69 对于最小译码单元$W_2$信道。假设，我们已知信道$W$的转移矩阵，即可通过$y_i$求得$P(\\hat x_i=0)$和 $P(\\hat x_i=1)$。$x_i$的似然比为 $$ LR(\\hat x_i) = \\frac{P(\\hat x_i=0)}{P(\\hat x_i=1)} $$ 这时可以得出$\\hat u_1$和$\\hat u_2$的似然比： $$ \\begin{align*} LR(\\hat u_1)\u0026=\\frac{P(\\hat u_1=0)}{P(\\hat u_1=1)}\\\\ \u0026=\\frac{P(\\hat x_1=0)P(\\hat x_2=0)+P(\\hat x_1=1)P(\\hat x_2=1)} {P(\\hat x_1=0)P(\\hat x_2=1)+P(\\hat x_1=1)P(\\hat x_2=0)}\\\\ \u0026=\\frac{1+LR(\\hat x_1)LR(\\hat x_2)}{LR(\\hat x_1)+LR(\\hat x_2)}\\\\ \\end{align*} $$ 由于译码$\\hat u_2$时$\\hat u_1$已知，因此需要分类讨论： $$ LR(\\hat u_2)= \\begin{cases} \\frac{P(\\hat x_1=0)P(\\hat x_2=0)}{P(\\hat x_1=1)P(\\hat x_2=1)} =LR(\\hat x_1)LR(\\hat x_2) \u0026\\hat u_1=0\\\\ \\frac{P(\\hat x_1=1)P(\\hat x_2=0)}{P(\\hat x_1=0)P(\\hat x_2=1)} =\\frac{LR(\\hat x_2)}{LR(\\hat x_1)} \u0026\\hat u_1=1\\\\ \\end{cases} $$ 于是有$\\hat u_1$和$\\hat u_2$的似然比： $$ \\left\\{ \\begin{align*} LR(\\hat u_1)\u0026=\\frac{1+LR(\\hat x_1)LR(\\hat x_2)}{LR(\\hat x_1)+LR(\\hat x_2)}\\\\ LR(\\hat u_2)\u0026=[LR(\\hat x_1)]^{1-2\\hat u_1}LR(\\hat x_2) \\end{align*} \\right. $$ 由于串行消除译码有前面比特判决出错影响后续判决的缺点，Tal等人提出了SCL译码算法。SCL译码在SC译码算法基础上做出改进，采用多条译码路径，克服了SC译码算法的缺点。其他译码算法还有PC-SCL、Fast-PC-SCL等10\n总结 总的来说，极化码的提出不仅是对香农编码定理的一大重要扩展，更是促进了人类社会的进步。极化码的优势在于它是唯一被严格证明可达到香农极限的编码方案，低复杂度编译码、灵活码率适配能力和低时延等特性使得它在众多编译码方案中脱颖而出。极化码虽已在5G控制信道中应用，但在走向更广泛实用的过程中，仍面临几个关键挑战。其在译码延迟问题、长码性能与复杂度平衡、信道估计敏感、硬件实现等方面亟待解决。极化码作为编码理论的里程碑，在5G及未来通信中仍具重要地位，但其实际部署需持续平衡性能、复杂度和工程可行性。11\nC. E. Shannon. A mathematical theory of communication[J]. The Bell System Technical Journal\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n樊昌信，曹丽娜. 通信原理(第七版)[M]. 北京：国防工业出版社，1980.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n深度求索.DeepSeek-V3.2. \u0026ldquo;为我讲述Turbo码的缺点和其未被选为5G信道编码技术的原因\u0026rdquo;[CP/OL]. (2025-12-20). https://chat.deepseek.com.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n深度求索.DeepSeek-V3.2. \u0026ldquo;5G技术两种信道编码的特点\u0026rdquo;[CP/OL]. (2025-12-20). https://chat.deepseek.com.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nE. Arikan. Channel Polarization: A Method for Constructing Capacity-Achieving Codes for Symmetric Binary-Input Memoryless Channels[J]. IEEE Transactions on Information Theory\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n里奇流. 5G 信道编码技术—Polar码[EB/OL]. (2023-04-17)[2025-12-20]. 知乎. https://zhuanlan.zhihu.com/p/622619502\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n张小飞，邵汉钦，吴启晖，徐大专. 信息论与编码[M]. 北京：电子工业出版社，2018\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n陈小龙. 极化码的编译码算法研究及FPGA实现[D]. 西安：西安电子科技大学，2025\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOrhan Gazi. Polar Codes: A Non-Trivial Approach to Channel Coding[M]. Springer Topics in Signal Processing，2019\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n李俊毅，邢莉娟，李卓. 奇偶校验极化码的快速串行抵消列表译码算法[A]. 《电子学报》\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n深度求索.DeepSeek-V3.2. \u0026ldquo;极化码的优点和挑战\u0026rdquo;[CP/OL]. (2025-12-20). https://chat.deepseek.com\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-05-17T00:00:00Z","permalink":"https://blog.detail.eu.org/p/7465b9/","title":"极化码"},{"content":"原理图 下图电路为异步Buck电路，将图中二极管更换为MOS管则为同步Buck电路。 开关导通 开关导通时，二极管不导通，电感$\\frac{\\mathrm{d}i}{\\mathrm{d}t}=\\frac{V_i-V_o}{L}$为一常数。电流基本呈线性增大。\n开关断开 开关断开时，二极管导通，电感$\\frac{\\mathrm{d}i}{\\mathrm{d}t}=-\\frac{V_d+V_o}{L}$为一常数。电流基本呈线性减小。\n设计目标 已知条件 输入电压 $V_i$ 输出电压 $V_o$ 开关频率 $f$ 输入纹波要求 $\\Delta V_i$ 输出纹波要求 $\\Delta V_o$ 负载电阻 $R$ 求解值 电感量 $L$ 输入滤波电容 $C_i$ 输出滤波电容 $C_o$ 设计计算 占空比 在一个周期内电感电流增加量和减少量应一致。根据上文开关打开与关闭时，电感电流对时间的导数的表达式可得下式：\n$$ \\frac{T_{on}}{T_{off}}=\\frac{V_o+V_d}{V_i-V_o} $$变换得到伏秒法则：\n$$ \\begin{gather} V_{L_{on}}\\cdot T_{on}=V_{L_{off}}\\cdot T_{off}\\\\ 其中 \\begin{cases} V_{L_{on}}\u0026=V_i-V_o\\\\ V_{L_{off}}\u0026=V_o+V_d \\end{cases} \\end{gather} $$再根据$T=T_{on}+T_{off}=\\frac1T$得： $$ \\begin{align*} \u0026\\begin{cases} T_{on}=\\frac{V_o+V_d}{V_i+V_d}\\cdot\\frac1f \\\\ T_{off}=\\frac{V_i-V_o}{V_i+V_d}\\cdot\\frac1f\\\\ \\end{cases}\\\\ \u0026D=\\frac{T_{on}}T=\\frac{V_o+V_d}{V_i+V_d}\\qquad 对于同步Buck:\\ V_d=0 \\end{align*} $$电感选型 在电感的选择上需要考虑两个参数\n电感感量 $L$ 电感电流 $I_L$ 平均电流 $I_L^*$ 纹波电流 $\\Delta I_L$ 为保证输出电容电压恒定： $$ I_L=I_o=\\frac{V_o}R $$开关导通时$U=V_i-V_o$ 导通时间$\\Delta t=T_{on}=\\frac{V_o+V_d}{V_i+V_d}\\cdot\\frac1f$ 因此$\\Delta I_L = U \\cdot \\frac{\\Delta t}L=\\frac{V_o+V_d}{V_i+V_d}\\cdot\\frac1{f\\cdot L}\\cdot (V_i-V_o)$ 即：\n$$ \\Delta I_L=\\frac{V_o+V_d}{f\\cdot L}\\cdot\\frac{V_i-V_o}{V_i+V_d}\\qquad 对于同步Buck:\\ \\Delta I_L=\\frac{V_o}{f\\cdot L}\\cdot(1-\\frac{V_o}{V_i}) $$电感峰值电流$I_{LP}=I_o +\\frac{\\Delta I_L}2$，在设计中要留有一定余量 即：\n$$ \\begin{align*} \u0026I_{LP}=I_o+\\frac{V_o+V_d}{2\\cdot f\\cdot L}\\cdot\\frac{V_i-V_o}{V_i+V_d}\\\\ \u0026对于同步Buck:\\ I_{LP}=I_o+\\frac{V_o}{2\\cdot f\\cdot L}\\cdot(1-\\frac{V_o}{V_i}) \\end{align*} $$在设计中电感纹波电流$\\Delta I_L$应该是$I_L$的 ==20%~40%== 为宜，即$\\Delta I_L=(0.2\\sim 0.4)\\cdot I_L$ 因此即可得出电感的感值：\n$$ \\begin{align*} \u0026L=\\frac{V_o+V_d}{f\\cdot(0.2\\sim 0.4)\\cdot I_o}\\cdot\\frac{V_i-V_o}{V_i+V_d}\\\\ \u0026对于同步Buck:\\ L=\\frac{V_o}{f\\cdot(0.2\\sim 0.4)\\cdot I_o}\\cdot(1-\\frac{V_o}{V_i}) \\end{align*} $$输入滤波电容 实际应用中，输入电源到Buck电路间有较长的导线，导线有寄生电感。导致电源对输入的响应不能达到Buck电路需求，因此需要选择合适的输入电容。\n","date":"2024-12-08T00:00:00Z","permalink":"https://blog.detail.eu.org/p/6713df/","title":"Buck电路"},{"content":"PID概述 激励为系统输入与输出之差\n$$ e(t)=R_{in}(t)-Y{out}(t) $$PID公式 $$ u(t)=K_p\\cdot err(t)+K_i\\int_{0}^{t}err(t)\\,dx+K_d\\frac{d\\,err(t)}{dt} $$$$ u(t)=K_c\\,[e(t)+\\frac{1}{T_i}\\int_{0}^{t}e(t)\\,dt+T_d\\frac{d\\,e(t)}{dt}] $$有:\n$$ K_i=\\frac{K_c}{T_i} $$$$ K_d=K_c\\cdot T_d $$Ziegler-Nichols方法 先给定一个$K_{cr}$（控制器增益）值，使系统处于临界震荡。 测定系统震荡周期$T_{cr}$ 计算得到$K_c$、$T_i$和$T_d$的值 基于上一步得到的值微调参数 控制方式 $K_c$ $T_i$ $T_d$ P $0.5K_{cr}$ $\\infty$ $0$ PI $0.45K_{cr}$ $0.83T_{cr}$ $0$ PD $0.8K_{cr}$ $\\infty$ $0.12T_{cr}$ PID $0.6K_{cr}$ $0.5T_{cr}$ $0.12T_{cr}$ 以$0.5K_{cr}$为基础\n增大$K_i$（即减小$T_i$），需微调增大$K_c$ 增大$K_d$（即增大$T_d$），需微调减小$K_c$ ","date":"2024-12-08T00:00:00Z","image":"https://blog.detail.eu.org/p/8cebd1/pid_hu_714bc5e6963d02a6.jpg","permalink":"https://blog.detail.eu.org/p/8cebd1/","title":"PID"},{"content":"SPI简介 SPI（Serial Peripheral Interface）是由Motorola公司开发的一种通用数据总线 四根通信线：SCK（Serial Clock）、MOSI（Master Output Slave Input）、MISO（Master Input Slave Output）、SS（Slave Select） 同步，全双工 支持总线挂载多设备（一主多从） 硬件电路 所有SPI设备的SCK、MOSI、MISO分别连在一起 主机另外引出多条SS控制线，分别接到各从机的SS引脚 输出引脚配置为推挽输出，输入引脚配置为浮空或上拉输入 SPI时序单元 起始条件：SS从高电平切换到低电平 终止条件：SS从低电平切换到高电平 交换一个字节（模式0） CPOL=0：空闲状态时，SCK为低电平 CPHA=0：SCK第一个边沿移入数据，第二个边沿移出数据 交换一个字节（模式1） CPOL=0：空闲状态时，SCK为低电平 CPHA=1：SCK第一个边沿移出数据，第二个边沿移入数据 交换一个字节（模式2） CPOL=1：空闲状态时，SCK为高电平 CPHA=0：SCK第一个边沿移入数据，第二个边沿移出数据 交换一个字节（模式3） CPOL=1：空闲状态时，SCK为高电平 CPHA=1：SCK第一个边沿移出数据，第二个边沿移入数据 SPI时序 发送指令 向SS指定的设备，发送指令（0x06） 指定地址写 向SS指定的设备，发送写指令（0x02），随后在指定地址（Address[23:0]）下，写入指定数据（Data） 指定地址读 向SS指定的设备，发送读指令（0x03），随后在指定地址（Address[23:0]）下，读取从机数据（Data） SPI外设 STM32内部集成了硬件SPI收发电路，可以由硬件自动执行时钟生成、数据收发等功能，减轻CPU的负担 可配置8位/16位数据帧、高位先行/低位先行 时钟频率： fPCLK / (2, 4, 8, 16, 32, 64, 128, 256) 支持多主机模型、主或从操作 可精简为半双工/单工通信 支持DMA 兼容I2S协议 实际外设: STM32F103C8T6 硬件SPI资源：SPI1、SPI2\n代码 软件SPI 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 void MySPI_W_SS(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); } void MySPI_W_SCK(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue); } void MySPI_W_MOSI(uint8_t BitValue) { GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue); } uint8_t MySPI_R_MISO(void) { return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6); } void MySPI_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); MySPI_W_SS(1); MySPI_W_SCK(0); } void MySPI_Start(void) { MySPI_W_SS(0); } void MySPI_Stop(void) { MySPI_W_SS(1); } uint8_t MySPI_SwapByte(uint8_t ByteSend) { uint8_t i, ByteReceive = 0x00; for (i = 0; i \u0026lt; 8; i ++) { MySPI_W_MOSI(ByteSend \u0026amp; (0x80 \u0026gt;\u0026gt; i)); MySPI_W_SCK(1); if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 \u0026gt;\u0026gt; i);} MySPI_W_SCK(0); } return ByteReceive; } 硬件SPI 待完成\n","date":"2024-02-26T00:00:00Z","permalink":"https://blog.detail.eu.org/p/fd124b/","title":"STM32 SPI"},{"content":"Markdown示例 标题 三级标题 四级标题 五级标题 六级标题 引用 这是一段引用\n这是另一段引用\n列表 有序列表 第一步 第二步 第三步 无序列表 元素1 元素2 元素3 任务列表 任务1 任务2 任务3 代码块 C 1 2 3 4 5 6 #include \u0026lt;stdio.h\u0026gt; int main(){ printf(\u0026#34;Hello World\u0026#34;); return 0 } C++ 1 2 3 4 5 6 #include \u0026lt;iostream\u0026gt; int main(){ std::cout \u0026lt;\u0026lt; \u0026#34;Hello World\u0026#34; \u0026lt;\u0026lt; std::endl; return 0; } Python 1 print(\u0026#34;Hello World\u0026#34;) 公式 $$ y=\\frac{\\partial f}{\\partial x}=e^{2\\sqrt{x}} $$表格 姓名 年龄 成绩 张三 19 98 李四 20 70 王五 18 59 脚注 这是一个段落，这是内容1。\n分割线 链接 显示名称\nhttps://www.bilibili.com/\n图片 )\n行内格式 斜体\n加粗\n下划线\nprint(\u0026quot;Hello World\u0026quot;)\t#代码\n:smile: :sob: :joy:\t表情符号大全\n$\\theta=x^2$\nH2O\nX^2^\n==高亮==\nBiliBili(Hugo Rich Content) 这是内容的脚注。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-02-22T00:00:00Z","permalink":"https://blog.detail.eu.org/p/bf5f6c/","title":"Markdown示例"},{"content":"I2C简介 I2C（Inter IC Bus）是由Philips公司开发的一种通用数据总线 两根通信线：SCL（Serial Clock）、SDA（Serial Data） 同步，半双工 带数据应答 支持总线挂载多设备（一主多从、多主多从） 硬件电路 所有I2C设备的SCL连在一起，SDA连在一起 设备的SCL和SDA均要配置成开漏输出模式 SCL和SDA各添加一个上拉电阻，阻值一般为4.7KΩ左右 I2C时序基本单元 起始条件：SCL高电平期间，SDA从高电平切换到低电平 终止条件：SCL高电平期间，SDA从低电平切换到高电平 发送一个字节：SCL低电平期间，主机将数据位依次放到SDA线上（高位先行），然后释放SCL，从机将在SCL高电平期间读取数据位，所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可发送一个字节 接收一个字节：SCL低电平期间，从机将数据位依次放到SDA线上（高位先行），然后释放SCL，主机将在SCL高电平期间读取数据位，所以SCL高电平期间SDA不允许有数据变化，依次循环上述过程8次，即可接收一个字节（主机在接收之前，需要释放SDA） 发送应答：主机在接收完一个字节之后，在下一个时钟发送一位数据，数据0表示应答，数据1表示非应答 接收应答：主机在发送完一个字节之后，在下一个时钟接收一位数据，判断从机是否应答，数据0表示应答，数据1表示非应答（主机在接收之前，需要释放SDA） I2C时序 指定地址写 对于指定设备（Slave Address），在指定地址（Reg Address）下，写入指定数据（Data）\n当前地址读 对于指定设备（Slave Address），在当前地址指针指示的地址下，读取从机数据（Data）\n指定地址读 对于指定设备（Slave Address），在指定地址（Reg Address）下，读取从机数据（Data）\nI2C外设 STM32内部集成了硬件I2C收发电路，可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能，减轻CPU的负担 支持多主机模型 支持7位/10位地址模式 支持不同的通讯速度，标准速度(高达100 kHz)，快速(高达400 kHz) 支持DMA 兼容SMBus协议 实际外设: STM32F103C8T6 硬件I2C资源：I2C1、I2C2\n序列图 代码 软件I2C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 /*硬件*/ // SCL写 void MyI2C_W_SCL(uint8_t BitValue){ GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); Delay_us(10); } // SDA写 void MyI2C_W_SDA(uint8_t BitValue){ GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); Delay_us(10); } // SDA读 uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); Delay_us(10); return BitValue; } //GPIO配置 void MyI2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, \u0026amp;GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); } /*协议*/ //开始标志 void MyI2C_Start(void){ MyI2C_W_SDA(1); MyI2C_W_SCL(1); MyI2C_W_SDA(0); MyI2C_W_SCL(0); } //结束标志 void MyI2C_Stop(void) { MyI2C_W_SDA(0); MyI2C_W_SCL(1); MyI2C_W_SDA(1); } //发送数据 void MyI2C_SendByte(uint8_t Byte){ uint8_t i; for (i = 0; i \u0026lt; 8; i ++) { MyI2C_W_SDA(Byte \u0026amp; (0x80 \u0026gt;\u0026gt; i)); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } } //接收数据 uint8_t MyI2C_ReceiveByte(void){ uint8_t i, Byte = 0x00; MyI2C_W_SDA(1); for (i = 0; i \u0026lt; 8; i ++) { MyI2C_W_SCL(1); if (MyI2C_R_SDA() == 1){ Byte |= (0x80 \u0026gt;\u0026gt; i); } MyI2C_W_SCL(0); } return Byte; } //应答信号发送 void MyI2C_SendAck(uint8_t AckBit) { MyI2C_W_SDA(AckBit); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } //应答信号接收 uint8_t MyI2C_ReceiveAck(void) { uint8_t AckBit; MyI2C_W_SDA(1); MyI2C_W_SCL(1); AckBit = MyI2C_R_SDA(); MyI2C_W_SCL(0); return AckBit; } 硬件I2C 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 //硬件地址 #define ADDRESS 0xD0 //时钟配置 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //GPIO配置 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, \u0026amp;GPIO_InitStructure); //I2C配置 I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed = 50000; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_Init(I2C2, \u0026amp;I2C_InitStructure); //I2C使能 I2C_Cmd(I2C2, ENABLE); //等待事件 void I2C_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t Timeout; Timeout = 10000; while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS) { Timeout --; if (Timeout == 0){ /*超时的错误处理代码，可以添加到此处*/ break; } } } void I2C_WriteReg(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C2, ENABLE); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); I2C_Send7bitAddress(I2C2, ADDRESS, I2C_Direction_Transmitter); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); I2C_SendData(I2C2, RegAddress); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); I2C_SendData(I2C2, Data); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); I2C_GenerateSTOP(I2C2, ENABLE); } uint8_t I2C_ReadReg(uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2, ENABLE); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); I2C_Send7bitAddress(I2C2, ADDRESS, I2C_Direction_Transmitter); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); I2C_SendData(I2C2, RegAddress); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); I2C_GenerateSTART(I2C2, ENABLE); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); I2C_Send7bitAddress(I2C2, ADDRESS, I2C_Direction_Receiver); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); I2C_AcknowledgeConfig(I2C2, DISABLE); I2C_GenerateSTOP(I2C2, ENABLE); I2C_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); Data = I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2, ENABLE); return Data; } I2C_InitTypeDef为I2C配置结构体 I2C_Mode为I2C模式 I2C_ClockSpeed为时钟频率 I2C_DutyCycle为I2C快速模式占空比 I2C_Ack为响应使能 I2C_AcknowledgedAddress为地址模式 I2C_OwnAddress1为本设备的第一个ID I2C_GenerateSTART函数发送起始条件 I2C_Send7bitAddress函数发送7位地址 I2C_SendData函数发送数据 I2C_GenerateSTOP函数发送终止条件 I2C_AcknowledgeConfig函数配置应答 I2C_CheckEvent函数读取状态 I2C_ReceiveData函数读取结果 I2C_EVENT 事件 值 I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED EV1 ((uint32_t)0x00060082) I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED EV1 ((uint32_t)0x00020002) I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED EV1 ((uint32_t)0x00860080) I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED EV1 ((uint32_t)0x00820000) I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED EV1 ((uint32_t)0x00120000) I2C_EVENT_SLAVE_BYTE_RECEIVED EV2 ((uint32_t)0x00020040) (I2C_EVENT_SLAVE_BYTE_RECEIVED| I2C_FLAG_DUALF) EV2 (I2C_EVENT_SLAVE_BYTE_RECEIVED| I2C_FLAG_GENCALL) EV2 I2C_EVENT_SLAVE_BYTE_TRANSMITTED EV3 ((uint32_t)0x00060084) (I2C_EVENT_SLAVE_BYTE_TRANSMITTED| I2C_FLAG_DUALF) EV3 (I2C_EVENT_SLAVE_BYTE_TRANSMITTED| I2C_FLAG_GENCALL) EV3 I2C_EVENT_SLAVE_ACK_FAILURE EV3_2 ((uint32_t)0x00000400) I2C_EVENT_SLAVE_STOP_DETECTED EV4 ((uint32_t)0x00000010) I2C_EVENT_MASTER_MODE_SELECT EV5 I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED EV6 I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED EV6 I2C_EVENT_MASTER_BYTE_RECEIVED EV7 I2C_EVENT_MASTER_BYTE_TRANSMITTING EV8 I2C_EVENT_MASTER_BYTE_TRANSMITTED EV8_2 I2C_EVENT_MASTER_MODE_ADDRESS10 EV9 I2C事件流程（常用的主模式） 通讯开始 EV5 I2C_EVENT_MASTER_MODE_SELECT After sending the START condition (I2C_GenerateSTART() function) the master has to wait for this event. It means that the Start condition has been correctly released on the I2C bus (the bus is free, no other devices is communicating). 发送 通讯启动条件（I2C_GenerateSTART() 函数）后，主设备必须等待此事件（EV5）。 此事件意味着I2C总线上的启动条件已正确释放（总线空闲，没有其他设备正在通信）。 地址确认 EV6 I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED After checking on EV5 (start condition correctly released on the bus), the master sends the address of the slave(s) with which it will communicate (I2C_Send7bitAddress() function, it also determines the direction of the communication: Master transmitter or Receiver). Then the master has to wait that a slave acknowledges his address. If an acknowledge is sent on the bus, one of the following events will be set: In case of Master Receiver (7-bit addressing): the I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED event is set. In case of Master Transmitter (7-bit addressing): the I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED is set In case of 10-Bit addressing mode, the master (just after generating the START and checking on EV5) has to send the header of 10-bit addressing mode (I2C_SendData() function). Then master should wait on EV9. It means that the 10-bit addressing header has been correctly sent on the bus. Then master should send the second part of the 10-bit address (LSB) using the function I2C_Send7bitAddress(). Then master should wait for event EV6. 主设备发送与其通信的从设备的地址，同时确定通信方向，然后主设备应该等待事件EV6。 通讯事件 EV8、EV8_2 / EV7 I2C_EVENT_MASTER_BYTE_TRANSMITTING、I2C_EVENT_MASTER_BYTE_TRANSMITTED / I2C_EVENT_MASTER_BYTE_RECEIVED Others If a communication is established (START condition generated and slave address acknowledged) then the master has to check on one of the following events for communication procedures:\nMaster Receiver mode: The master has to wait on the event EV7 then to read the data received from the slave (I2C_ReceiveData() function). Master Transmitter mode: The master has to send data (I2C_SendData() function) then to wait on event EV8 or EV8_2. These two events are similar: EV8 means that the data has been written in the data register and is being shifted out. EV8_2 means that the data has been physically shifted out and output on the bus. In most cases, using EV8 is sufficient for the application. Using EV8_2 leads to a slower communication but ensure more reliable test. EV8_2 is also more suitable than EV8 for testing on the last data transmission (before Stop condition generation). In case the user software does not guarantee that this event EV7 is managed before the current byte end of transfer, then user may check on EV7 and BTF flag at the same time (ie. (I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_FLAG_BTF)). In this case the communication may be slower. ","date":"2024-02-20T00:00:00Z","permalink":"https://blog.detail.eu.org/p/094b50/","title":"STM32 I2C"},{"content":"DMA简介 DMA（Direct Memory Access）直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输，无须CPU干预，节省了CPU的资源 12个独立可配置的通道： DMA1（7个通道）， DMA2（5个通道） 每个通道都支持软件触发和特定的硬件触发 STM32F103C8T6 DMA资源：DMA1（7个通道） DMA数据传输宽度与大小 DMA数据传输: 源端大于目标，高位舍弃。 源端小于目标，高位补零。\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 //全局变量，用于存储每次转运的字节数 uint16_t MyDMA_Size; //配置时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA配置 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryBaseAddr = AddrB; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = Size; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_M2M = DMA_M2M_Enable; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_Init(DMA1_Channel1, \u0026amp;DMA_InitStructure); //DMA使能（这里未使能） DMA_Cmd(DMA1_Channel1, DISABLE); void MyDMA_Transfer(void) { DMA_Cmd(DMA1_Channel1, DISABLE); DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size); DMA_Cmd(DMA1_Channel1, ENABLE); while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); DMA_ClearFlag(DMA1_FLAG_TC1); } DMA_InitTypeDef为DMA配置结构体\n外设配置： DMA_PeripheralBaseAddr为外设基地址，转运来源 DMA_PeripheralDataSize为外设数据宽度 DMA_PeripheralInc为外设地址自增： DMA_PeripheralInc_Enable开启 DMA_PeripheralInc_Disable关闭 存储器配置： DMA_MemoryBaseAddr为存储器基地址，转运目标 DMA_MemoryDataSize为存储器数据宽度 DMA_MemoryInc为存储器地址自增： DMA_MemoryInc_Enable开启 DMA_MemoryInc_Disable关闭 DMA_DIR为数据传输方向： DMA_DIR_PeripheralDST存储器到外设 DMA_DIR_PeripheralSRC外设到存储器 DMA_BufferSize为转运的数据大小（转运次数） DMA_Mode为模式，主要配置自动重装 DMA_Mode_Circular循环模式 DMA_Mode_Normal单次模式 DMA_M2M软硬件触发选择 DMA_M2M_Enable开启 DMA_M2M_Disable关闭 DMA_Priority优先级 DMA_PeripheralDataSize 值 描述 DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) 1Byte DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000100) 2Byte DMA_PeripheralDataSize_Word ((uint32_t)0x00000200) 4Byte DMA_MemoryDataSize 值 描述 DMA_MemoryDataSize_Byte ((uint32_t)0x00000000) 1Byte DMA_MemoryDataSize_HalfWord ((uint32_t)0x00000400) 2Byte DMA_MemoryDataSize_Word ((uint32_t)0x00000800) 4Byte DMA_Priority 值 描述 DMA_Priority_VeryHigh ((uint32_t)0x00003000) 非常高 DMA_Priority_High ((uint32_t)0x00002000) 高 DMA_Priority_Medium ((uint32_t)0x00001000) 中 DMA_Priority_Low ((uint32_t)0x00000000) 低 DMA + ADC 协同工作 DMA请求 更多DMA请求映像的相关内容：STM32F10xxx参考手册（中文）.pdf 页码147/手册10.3.7\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 //存储数值的全局变量 uint16_t AD_Value[4]; //时钟配置 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); //GPIO配置 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //ADC通道配置 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //ADC配置 ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_NbrOfChannel = 4; ADC_Init(ADC1, \u0026amp;ADC_InitStructure); //DMA配置 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)\u0026amp;ADC1-\u0026gt;DR; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; MA_InitStructure.DMA_BufferSize = 4; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; DMA_Init(DMA1_Channel1, \u0026amp;DMA_InitStructure); //DMA和ADC使能 DMA_Cmd(DMA1_Channel1, ENABLE); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); //ADC校准 ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET); //ADC触发 ADC_SoftwareStartConvCmd(ADC1, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE);启用DMA通道1 ADC_DMACmd(ADC1, ENABLE);启用ADC1触发DMA1信号 ADC_Cmd(ADC1, ENABLE);启用ADC1 ","date":"2024-02-17T00:00:00Z","permalink":"https://blog.detail.eu.org/p/173c10/","title":"STM32 DMA"},{"content":"通信接口 通信的目的：将一个设备的数据传送到另一个设备，扩展硬件系统 通信协议：制定通信的规则，通信双方按照协议规则进行数据收发 名称 引脚 双工 时钟 电平 设备 USART TX、RX 全双工 异步 单端 点对点 I2C SCL、SDA 半双工 同步 单端 多设备 SPI SCLK、MOSI、MISO、CS 全双工 同步 单端 多设备 CAN CAN_H、CAN_L 半双工 异步 差分 多设备 USB DP、DM 半双工 异步 差分 点对点 串口 串口是一种应用十分广泛的通讯接口，串口成本低、容易使用、通信线路简单，可实现两个设备的互相通信 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信，极大地扩展了单片机的应用范围，增强了单片机系统的硬件实力 硬件电路 简单双向串口通信有两根通信线（发送端TX和接收端RX） TX与RX要交叉连接 当只需单向的数据传输时，可以只接一根通信线 当电平标准不一致时，需要加电平转换芯片 电平标准 电平标准是数据1和数据0的表达方式，是传输线缆中人为规定的电压与数据的对应关系，串口常用的电平标准有如下三种：\nTTL电平：+3.3V或+5V表示1，0V表示0 RS232电平：-3~-15V表示1，+3~+15V表示0 RS485电平：两线压差+2~+6V表示1，-2~-6V表示0（差分信号） 串口参数及时序 波特率：串口通信的速率 起始位：标志一个数据帧的开始，固定为低电平 数据位：数据帧的有效载荷，1为高电平，0为低电平，低位先行 校验位：用于数据验证，根据数据位计算得来 停止位：用于数据帧间隔，固定为高电平 USART简介 USART（Universal Synchronous/Asynchronous Receiver/Transmitter）通用同步/异步收发器 USART是STM32内部集成的硬件外设，可根据数据寄存器的一个字节数据自动生成数据帧时序，从TX引脚发送出去，也可自动接收RX引脚的数据帧时序，拼接为一个字节数据，存放在数据寄存器里 自带波特率发生器，最高达4.5Mbits/s 可配置数据位长度（8/9）、停止位长度（0.5/1/1.5/2） 可选校验位（无校验/奇校验/偶校验） 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN 实际外设: STM32F103C8T6 USART资源：USART1、USART2、USART3\n更多USART的相关内容：STM32F10xxx参考手册（中文）.pdf 页码516/手册25\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 //数据接收全局变量 uint8_t Serial_RxData; uint8_t Serial_RxFlag; //时钟配置 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIO配置 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //TX GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //RX //USART配置 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART1, \u0026amp;USART_InitStructure); //中断配置 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(\u0026amp;NVIC_InitStructure); //USART使能 USART_Cmd(USART1, ENABLE); //发送字节 void Serial_SendByte(uint8_t Byte) { USART_SendData(USART1, Byte); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } //返回接收数据 uint8_t Serial_GetRxData(void) { return Serial_RxData; } void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { Serial_RxData = USART_ReceiveData(USART1); Serial_RxFlag = 1; USART_ClearITPendingBit(USART1, USART_IT_RXNE); } } USART_InitTypeDef为USART配置结构体 USART_BaudRate为波特率：常用9600、115200等 USART_HardwareFlowControl为硬件流控制（一般不用） USART_Mode为模式 USART_Parity为奇偶校验 USART_StopBits为停止位 USART_WordLength为字长 USART_ITConfig函数用于配置USART中断 USART_SendData函数用于发送数据 USART_ReceiveData函数用于读取接收到的数据 获取USART标志位 USART_GetFlagStatus标志位 USART_GetITStatus中断标志位 USART_HardwareFlowControl 值 描述 USART_HardwareFlowControl_None ((uint16_t)0x0000) 关闭 USART_HardwareFlowControl_RTS ((uint16_t)0x0100) 发送可接收信号 USART_HardwareFlowControl_CTS ((uint16_t)0x0200) 接收可发送信号 USART_HardwareFlowControl_RTS_CTS ((uint16_t)0x0300) 开启全部 USART_Mode 值 描述 USART_Mode_Rx ((uint16_t)0x0004) 发送 USART_Mode_Tx ((uint16_t)0x0008) 接收 USART_Parity 值 描述 USART_Parity_No ((uint16_t)0x0000) 不校验 USART_Parity_Even ((uint16_t)0x0400) 偶校验 USART_Parity_Odd ((uint16_t)0x0600) 奇校验 USART_StopBits 值 描述 USART_StopBits_1 ((uint16_t)0x0000) 1位停止位 USART_StopBits_0_5 ((uint16_t)0x1000) 0.5位停止位 USART_StopBits_2 ((uint16_t)0x2000) 2位停止位 USART_StopBits_1_5 ((uint16_t)0x3000) 1.5位停止位 USART_WordLength 值 描述 USART_WordLength_8b ((uint16_t)0x0000) 8位数据 USART_WordLength_9b ((uint16_t)0x1000) 9位数据 USART_ITs 值 USART_Flags 值 描述 USART_IT_PE ((uint16_t)0x0028) USART_FLAG_PE ((uint16_t)0x0001) 奇偶检验错 USART_IT_TXE ((uint16_t)0x0727) USART_FLAG_TXE ((uint16_t)0x0080) 发送数据寄存器空 USART_IT_TC ((uint16_t)0x0626) USART_FLAG_TC ((uint16_t)0x0040) 发送完成 USART_IT_RXNE ((uint16_t)0x0525) USART_FLAG_RXNE ((uint16_t)0x0020) 接收数据就绪可读 USART_IT_IDLE ((uint16_t)0x0424) USART_FLAG_IDLE ((uint16_t)0x0010) 检测到空闲线路 USART_IT_LBD ((uint16_t)0x0846) USART_FLAG_LBD ((uint16_t)0x0100) 断开标志 USART_IT_CTS ((uint16_t)0x096A) USART_FLAG_CTS ((uint16_t)0x0200) CTS标志 USART_IT_ERR ((uint16_t)0x0060) 未开启USART_IT_ERR时，只能由USART_GetFlagStatus(USARTx,USART_Flag_ORE) 检测到USART_IT_ORE；在开启USART_IT_ERR后，才能被 USART_GetITStatus(USARTx,USART_IT_ORE) 检测到USART_IT_ORE； USART_IT_ORE ((uint16_t)0x0360) USART_FLAG_ORE ((uint16_t)0x0008) 检测到数据溢出 USART_IT_NE ((uint16_t)0x0260) USART_FLAG_NE ((uint16_t)0x0004) 噪声标志 USART_IT_FE ((uint16_t)0x0160) USART_FLAG_FE ((uint16_t)0x0002) 帧错误 数据包设计 HEX固定包长 字符可变包长 ","date":"2024-02-17T00:00:00Z","permalink":"https://blog.detail.eu.org/p/e15d66/","title":"STM32 USART"},{"content":"ADC简介 ADC（Analog-Digital Converter）模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量，建立模拟电路到数字电路的桥梁 12位逐次逼近型ADC，1us转换时间 输入电压范围：0-3.3V，转换结果范围：0-4095 18个输入通道，可测量16个外部和2个内部信号源 规则组和注入组两个转换单元 模拟看门狗自动监测输入电压范围 STM32F103C8T6 ADC资源：ADC1、ADC2，10个外部输入通道 通道 ADC1 ADC2 ADC3 通道0 PA0 PA0 PA0 通道1 PA1 PA1 PA1 通道2 PA2 PA2 PA2 通道3 PA3 PA3 PA3 通道4 PA4 PA4 PF6 通道5 PA5 PA5 PF7 通道6 PA6 PA6 PF8 通道7 PA7 PA7 PF9 通道8 PB0 PB0 PF10 通道9 PB1 PB1 通道10 PC0 PC0 PC0 通道11 PC1 PC1 PC1 通道12 PC2 PC2 PC2 通道13 PC3 PC3 PC3 通道14 PC4 PC4 通道15 PC5 PC5 通道16 温度传感器 通道17 内部参考电压 转换模式 单次转换，非扫描模式 单次转换，非扫描模式 连续转换，非扫描模式 连续转换，非扫描模式 数据对齐 触发控制 AD转换的步骤：采样，保持，量化，编码 STM32 ADC的总转换时间为： $$ T_{CONV} = T_{sampling} + 12.5T_{ADC} $$ 例如：当ADCCLK=14MHz，采样时间为1.5个ADC周期 $$ T_{CONV} = (1.5 + 12.5)T_{ADC} = 14T_{ADC} = 1μs $$ 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 //配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); //GPIO配置 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //ADC配置 ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, \u0026amp;ADC_InitStructure); //ADC使能 ADC_Cmd(ADC1, ENABLE); //ADC校准 ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1) == SET); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) == SET); //获取值 uint16_t AD_GetValue(uint8_t ADC_Channel) { ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); return ADC_GetConversionValue(ADC1); } RCC_ADCCLKConfig函数用于配置ADC时钟分频（ADC最高支持14M时钟） GPIO_InitTypeDef为ADC配置结构体 ADC_Mode为ADC模式 ADC_DataAlign为数据对齐模式 ADC_ExternalTrigConv为外部触发模式 ADC_ContinuousConvMode为连续转换使能 ADC_ScanConvMode为扫描模式使能 ADC_NbrOfChannel为ADC通道数 ADC_ResetCalibration函数用于复位ADC校准寄存器 ADC_GetResetCalibrationStatus函数用于检查ADC校准寄存器状态 ADC_StartCalibration函数用于校准ADC ADC_GetCalibrationStatus函数用于检查ADC校准 ADC_RegularChannelConfig函数用于配置规则组通道 ADC_Channel为ADC通道：ADC_Channel_x ADC_SampleTime为采样时间 ADC_GetFlagStatus函数用于获取ADC标志位 ADC_GetConversionValue函数用于获取规则组结果 ADC_SoftwareStartConvCmd函数用于软件触发ADC RCC_PCLK2 值 描述 RCC_PCLK2_Div2 ((uint32_t)0x00000000) 二分频 RCC_PCLK2_Div4 ((uint32_t)0x00004000) 四分频 RCC_PCLK2_Div6 ((uint32_t)0x00008000) 六分频 RCC_PCLK2_Div8 ((uint32_t)0x0000C000) 八分频 ADC_Mode 值 描述 ADC_Mode_Independent ((uint32_t)0x00000000) 独立模式 ADC_Mode_RegInjecSimult ((uint32_t)0x00010000) 混合的同步规则+注入同步模式 ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000) 混合的同步规则+交替触发模式 ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000) 混合同步注入+快速交叉模式 ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000) 混合同步注入+慢速交叉模式 ADC_Mode_InjecSimult ((uint32_t)0x00050000) 注入同步模式 ADC_Mode_RegSimult ((uint32_t)0x00060000) 规则同步模式 ADC_Mode_FastInterl ((uint32_t)0x00070000) 快速交叉模式 ADC_Mode_SlowInterl ((uint32_t)0x00080000) 慢速交叉模式 ADC_Mode_AlterTrig ((uint32_t)0x00090000) 交替触发模式 更多ADC_Mode的相关内容：STM32F10xxx参考手册（中文）.pdf 页码163/手册11.9\nADC_DataAlign 值 描述 ADC_DataAlign_Right ((uint32_t)0x00000000) 右对齐 ADC_DataAlign_Left ((uint32_t)0x00000800) 左对齐 ADC_ExternalTrigConv 值 描述 ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) 内部信号 ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) 内部信号 ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) 内部信号 ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) 内部信号 ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) 内部信号 ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) 外部引脚/内部信号 ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) 内部信号 ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) 软件触发 ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) 内部信号 ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) 内部信号 ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) 内部信号 ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) 内部信号 ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) 内部信号 ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) 内部信号 前六个与后六个分别对应ADC12和ADC3，第七、八个可用于ADC123 更多外部触发相关内容：STM32F10xxx参考手册（中文）.pdf 页码162/手册11.7\nADC_SampleTime 值 描述 ADC_SampleTime_1Cycles5 ((uint8_t)0x00) Sample time equal to 1.5 cycles ADC_SampleTime_7Cycles5 ((uint8_t)0x01) Sample time equal to 7.5 cycles ADC_SampleTime_13Cycles5 ((uint8_t)0x02) Sample time equal to 13.5 cycles ADC_SampleTime_28Cycles5 ((uint8_t)0x03) Sample time equal to 28.5 cycles ADC_SampleTime_41Cycles5 ((uint8_t)0x04) Sample time equal to 41.5 cycles ADC_SampleTime_55Cycles5 ((uint8_t)0x05) Sample time equal to 55.5 cycles ADC_SampleTime_71Cycles5 ((uint8_t)0x06) Sample time equal to 71.5 cycles ADC_SampleTime_239Cycles5 ((uint8_t)0x07) Sample time equal to 239.5 cycles ","date":"2024-02-16T00:00:00Z","permalink":"https://blog.detail.eu.org/p/b36238/","title":"STM32 ADC"},{"content":"TIM简介 TIM（Timer）定时器 定时器可以对输入的时钟进行计数，并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元，在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能，而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型 实际外设: STM32F103C8T6定时器资源：TIM1、TIM2、TIM3、TIM4\n类型 编号 总线 功能 高级定时器 TIM1、TIM8 APB2 拥有通用定时器全部功能，并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 通用定时器 TIM2、TIM3、TIM4、TIM5 APB1 拥有基本定时器全部功能，并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 基本定时器 TIM6、TIM7 APB1 拥有定时中断、主模式触发DAC的功能 TIM结构 主要结构 预分频器 PSC 计数器 CNT 自动重装器 ARR 重复计数器 REP 高级定时器 通用定时器 基本定时器 定时器中断结构 定时器的触发模式 内部时钟触发：CK_INT 外部时钟触发（外部时钟模式2）：ETR 从模式触发（外部时钟模式1）：TRGI 编码器接口触发：TI1FP1\u0026amp;TI2FP2 时基单元 预分频器 计数器计数频率：\n$$ CK_{CNT}=\\frac{CK_{PSC}}{PSC+1} $$计数器 计数器溢出频率：\n$$ CK_{CNT_{OV}}=\\frac{CK_{CNT}}{ARR+1} $$$$ CK_{CNT_{OV}}=\\frac{CK_{PSC}}{(PSC+1)(ARR+1)} $$ARPE=0时，TIMx_ARR无预装入。ARR值即刻生效（不建议） ARPE=1时，TIMx_ARR有预装入。ARR值在下次重装时生效\n代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 //开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //配置TIM2为内部时钟 TIM_InternalClockConfig(TIM2); //时基单元初始化 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, \u0026amp;TIM_TimeBaseInitStructure); //中断配置 TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //NVIC配置 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(\u0026amp;NVIC_InitStructure); //使能定时器 TIM_Cmd(TIM2, ENABLE); //中断函数 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } 配置TIM时钟 选择内部时钟( INT )：void TIM_InternalClockConfig(TIM_TypeDef* TIMx); 选择其他定时器( ITRx )：void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); 选择外部引脚( TIx )：void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter); 选择外部时钟( ETR ) 模式1( TRGI )：void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); 模式2：void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); 主要参数：\nTIMx为TIM编号 TIM_InputTriggerSource为输入定时器源 TIM_TIxExternalCLKSource为外部输入引脚 TIM_ICPolarity为外部引脚极性选择 ICFilter为外部引脚滤波器：0x0-0xF TIM_ExtTRGPrescaler为外部时钟预分频器 TIM_ExtTRGPolarity为外部时钟极性 ExtTRGFilter为外部时钟滤波器：0x0-0xF TIM_InputTriggerSource 值 描述 TIM_TS_ITR0 ((uint16_t)0x0000) TIM内部触发0 TIM_TS_ITR1 ((uint16_t)0x0010) TIM内部触发1 TIM_TS_ITR2 ((uint16_t)0x0020) TIM内部触发2 TIM_TS_ITR3 ((uint16_t)0x0030) TIM内部触发3 TIM_TIxExternalCLKSource 值 描述 TIM_TS_TI1FP1 ((uint16_t)0x0050) TIMIC1连接TI1（即使用TIMx_CH1） TIM_TS_TI2FP2 ((uint16_t)0x0060) TIMIC2连接TI2（即使用TIMx_CH2） TIM_TS_TI1F_ED ((uint16_t)0x0040) TIMIC1连接TI1，使用边缘检测 TIM_ExtTRGPrescaler 值 描述 TIM_ExtTRGPSC_OFF ((uint16_t)0x0000) TIM ETRP 不分频 TIM_ExtTRGPSC_DIV2 ((uint16_t)0x1000) TIM ETRP 二分频 TIM_ExtTRGPSC_DIV4 ((uint16_t)0x2000) TIM ETRP 四分频 TIM_ExtTRGPSC_DIV8 ((uint16_t)0x3000) TIM ETRP 八分频 TIM_ICPolarity 值 描述 TIM_ICPolarity_Rising ((uint16_t)0x0000) 上升沿有效 TIM_ICPolarity_Falling ((uint16_t)0x0002) 下降沿有效 TIM_ICPolarity_BothEdge ((uint16_t)0x000A) 双边缘有效 TIM_ICPolarity 值 描述 TIM_ExtTRGPolarity_Inverted ((uint16_t)0x8000) 极性翻转，低电平或下降沿有效 TIM_ExtTRGPolarity_NonInverted ((uint16_t)0x0000) 极性不翻转，高电平或上升沿有效 配置时基单元 TIM_ClockDivision为时钟分频，与预分频器作用类似，但作用有限 TIM_CounterMode为计数方式 TIM_Period为自动重装值( ARR ) TIM_Prescaler为预分频系数( PSC ) TIM_RepetitionCounter为重复计数值( REP ，仅高级定时器有效) TIM_ClockDivision 值 描述 TIM_CKD_DIV1 ((uint16_t)0x0000) 不分频 TIM_CKD_DIV2 ((uint16_t)0x0100) 二分频 TIM_CKD_DIV4 ((uint16_t)0x0200) 四分频 TIM_CounterMode 值 描述 TIM_CounterMode_Up ((uint16_t)0x0000) 向上计数模式 TIM_CounterMode_Down ((uint16_t)0x0010) 向下计数模式 TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020) 中心对齐计数模式，向下计数时匹配 TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040) 中心对齐计数模式，向上计数时匹配 TIM_CounterMode_CenterAligned3 ((uint16_t)0x0060) 中心对齐计数模式，匹配所有 配置中断 在初始化时基单元后，会产生一个更新事件来装入自动重装值。因此需要用 TIM_ClearFlag清除中断标志位（当使用更新中断时） 定时器能产生多种中断，需要用 TIM_ITConfig配置 TIM_IT 值 描述 TIM_IT_Update ((uint16_t)0x0001) 更新中断，计数器向上溢出/向下溢出，计数器初始化 TIM_IT_CC1 ((uint16_t)0x0002) 捕获/比较中断，CH1 TIM_IT_CC2 ((uint16_t)0x0004) 捕获/比较中断，CH2 TIM_IT_CC3 ((uint16_t)0x0008) 捕获/比较中断，CH3 TIM_IT_CC4 ((uint16_t)0x0010) 捕获/比较中断，CH4 TIM_IT_Trigger ((uint16_t)0x0040) 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) 输出比较 OC（Output Compare）输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系，来对输出电平进行置1、置0或翻转的操作，用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道 高级定时器的前3个通道额外拥有死区生成和互补输出的功能 模式 描述 冻结 CNT=CCR时，REF保持为原状态 匹配时置有效电平 CNT=CCR时，REF置有效电平 匹配时置无效电平 CNT=CCR时，REF置无效电平 匹配时电平翻转 CNT=CCR时，REF电平翻转 强制为无效电平 CNT与CCR无效，REF强制为无效电平 强制为有效电平 CNT与CCR无效，REF强制为有效电平 PWM模式1 向上计数：CNT\u0026lt;CCR时，REF置有效电平，CNT≥CCR时，REF置无效电平\u0026lt;br\u0026gt;向下计数：CNT\u0026gt;CCR时，REF置无效电平，CNT≤CCR时，REF置有效电平 PWM模式2 向上计数：CNT\u0026lt;CCR时，REF置无效电平，CNT≥CCR时，REF置有效电平\u0026lt;br\u0026gt;向下计数：CNT\u0026gt;CCR时，REF置有效电平，CNT≤CCR时，REF置无效电平 以PWM模式1向上计数为例： PWM频率： $$ Freq=\\frac{CK_{PSC}}{(PSC+1)(ARR+1)} $$ PWM占空比： $$ Duty=\\frac{CCR}{ARR+1} $$ PWM分辨率： $$ Reso=\\frac{1}{ARR+1} $$代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 //开启时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIO初始化 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //时钟配置 TIM_InternalClockConfig(TIM2); //时基单元初始化 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, \u0026amp;TIM_TimeBaseInitStructure); //输出比较初始化 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(\u0026amp;TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OC1Init(TIM2, \u0026amp;TIM_OCInitStructure); //使能定时器 TIM_Cmd(TIM2, ENABLE); //CCR设置函数 void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM2, Compare); } //ARR设置函数 void PWM_SetAutoreload(uint16_t Autoreload) { TIM_SetAutoreload(TIM2, Autoreload); } //PSC设置函数 void PWM_SetPrescaler(uint16_t Prescaler) { TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Update); } TIM_OCInitTypeDef为输出比较配置结构体\nTIM_OCStructInit函数为结构体赋默认值（特别） TIM_SetComparex函数能设置CCRx寄存器值 TIM_SetAutoreload函数能设置ARR寄存器值 TIM_PrescalerConfig函数能设置PSC寄存器值： TIM_PSCReloadMode_Immediate立即生效 TIM_PSCReloadMode_Update更新事件后生效 TIM_OCxInit函数能配置OCx通道 TIM_OCMode为输出比较模式 TIM_OCPolarity为输出极性：TIM_OCPolarity_High正常，TIM_OCPolarity_Low翻转 TIM_OutputState为输出使能：TIM_OutputState_Enable开启，TIM_OutputState_Disable关闭 TIM_Pulse为CCR初始值 TIM_OCMode 值 描述 TIM_OCMode_Timing ((uint16_t)0x0000) 冻结 TIM_OCMode_Active ((uint16_t)0x0010) 匹配时置有效电平 TIM_OCMode_Inactive ((uint16_t)0x0020) 匹配时置无效电平 TIM_OCMode_Toggle ((uint16_t)0x0030) 匹配时电平翻转 TIM_OCMode_PWM1 ((uint16_t)0x0060) PWM模式1 TIM_OCMode_PWM2 ((uint16_t)0x0070) PWM模式2 输入捕获 IC（Input Capture）输入捕获 输入捕获模式下，当通道输入引脚出现指定电平跳变时，当前CNT的值将被锁存到CCR中，可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参 每个高级定时器和通用定时器都拥有4个输入捕获通道 可配置为PWMI模式，同时测量频率和占空比 可配合主从触发模式，实现硬件全自动测量 频率测量 测频法：在闸门时间T内，对上升沿计次，得到N，则频率\n$$ f_x = \\frac{N}{T} $$ 测周法：两个上升沿内，以标准频率fc计次，得到N ，则频率\n$$ f_x = \\frac{f_c}{N} $$ 中界频率：测频法与测周法误差相等的频率点\n$$ f_m = \\sqrt\\frac{f_c}{T} $$ PWMI（测周法） 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 //配置时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //配置GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //配置定时器 TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, \u0026amp;TIM_TimeBaseInitStructure); //配置捕获通道 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICFilter = 0xF; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_PWMIConfig(TIM3, \u0026amp;TIM_ICInitStructure); //TIM_ICInit(TIM3, \u0026amp;TIM_ICInitStructure); //配置从模式 TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //使能定时器 TIM_Cmd(TIM3, ENABLE); //获取频率 uint32_t IC_GetFreq(void) { return 1000000 / (TIM_GetCapture1(TIM3) + 1); } //获取占空比 uint32_t IC_GetDuty(void) { return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); } TIM_ICInitTypeDef为输入捕获配置结构体\nTIM_Channel为输入通道：TIM_Channel_x(x=1,2,3,4) TIM_ICFilter为输入滤波器：0x0-0xF TIM_ICPolarity为输入极性选择 TIM_ICPrescaler为预分频配置 TIM_ICSelection为信号交叉选择 TIM_PWMIConfig函数会对另一通道进行相应配置，TIM_ICInit函数只会配置当前通道 TIM_GetCapturex函数会获取CCRx寄存器的值 计算占空比时，对CCR1和CCR2的值+1的的操作可以避免除0，实际值应为\n$$ val_1=CCR1+0.5\\quad(\\pm0.5) $$$$ val_2=CCR2+0.5\\quad(\\pm0.5) $$$$ Freq=\\frac{CK_{PSC}}{(PSC+1)*val_1} $$$$ Duty=100\\%*\\frac{val_2}{val_1} $$主从触发 从模式 1 2 TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); TIM_SelectInputTrigger函数用于配置从模式触发源 TIM_SelectSlaveMode函数用于配置从模式 TIM_InputTriggerSource 值 描述 TIM_TS_ITR0 ((uint16_t)0x0000) Internal Trigger 0 TIM_TS_ITR1 ((uint16_t)0x0010) Internal Trigger 1 TIM_TS_ITR2 ((uint16_t)0x0020) Internal Trigger 2 TIM_TS_ITR3 ((uint16_t)0x0030) Internal Trigger 3 TIM_TS_TI1F_ED ((uint16_t)0x0040) TI1 Edge Detector TIM_TS_TI1FP1 ((uint16_t)0x0050) Filtered Timer Input 1 TIM_TS_TI2FP2 ((uint16_t)0x0060) Filtered Timer Input 2 TIM_TS_ETRF ((uint16_t)0x0070) External Trigger input TIM_SlaveMode 值 描述 TIM_SlaveMode_Reset ((uint16_t)0x0004) Rising edge of the selected trigger signal (TRGI) re-initializes\u0026lt;br\u0026gt; the counter and triggers an update of the registers. TIM_SlaveMode_Gated ((uint16_t)0x0005) The counter clock is enabled when the trigger signal (TRGI) is high. TIM_SlaveMode_Trigger ((uint16_t)0x0006) The counter starts at a rising edge of the trigger TRGI. TIM_SlaveMode_External1 ((uint16_t)0x0007) Rising edges of the selected trigger (TRGI) clock the counter. 主模式 1 2 TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); TIM_SelectMasterSlaveMode函数用于使能主模式： TIM_MasterSlaveMode_Enable开启 TIM_MasterSlaveMode_Disable关闭 TIM_SelectOutputTrigger函数用于配置触发源 TIM_TRGOSource 值 描述 TIM_TRGOSource_Reset ((uint16_t)0x0000) The UG bit in the TIM_EGR register is used as the trigger output (TRGO). TIM_TRGOSource_Enable ((uint16_t)0x0010) The Counter Enable CEN is used as the trigger output (TRGO). TIM_TRGOSource_Update ((uint16_t)0x0020) The update event is selected as the trigger output (TRGO). TIM_TRGOSource_OC1 ((uint16_t)0x0030) The trigger output sends a positive pulse when the CC1IF flag is to be set, as soon as a capture or compare match occurs (TRGO). TIM_TRGOSource_OC1Ref ((uint16_t)0x0040) OC1REF signal is used as the trigger output (TRGO). TIM_TRGOSource_OC2Ref ((uint16_t)0x0050) OC2REF signal is used as the trigger output (TRGO). TIM_TRGOSource_OC3Ref ((uint16_t)0x0060) OC3REF signal is used as the trigger output (TRGO). TIM_TRGOSource_OC4Ref ((uint16_t)0x0070) OC4REF signal is used as the trigger output (TRGO). 编码器接口 Encoder Interface 编码器接口 编码器接口可接收增量（正交）编码器的信号，根据编码器旋转产生的正交信号脉冲，自动控制CNT自增或自减，从而指示编码器的位置、旋转方向和旋转速度 每个高级定时器和通用定时器都拥有1个编码器接口 两个输入引脚借用了输入捕获的通道1和通道2 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 //配置时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIO配置（配置CH1和CH2） GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); //时基单元配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, \u0026amp;TIM_TimeBaseInitStructure); //通道配置（配置CH1和CH2） TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICStructInit(\u0026amp;TIM_ICInitStructure); TIM_ICInitStructure.TIM_ICFilter = 0xF; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInit(TIM3, \u0026amp;TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInit(TIM3, \u0026amp;TIM_ICInitStructure); //配置编码器接口 TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //使能定时器 TIM_Cmd(TIM3, ENABLE); int16_t Encoder_Get(void) { // int16_t Temp; // Temp = TIM_GetCounter(TIM3); // TIM_SetCounter(TIM3, 0); // return Temp; return TIM_GetCounter(TIM3); } TIM_EncoderInterfaceConfig为编码器配置函数 TIM_ICxPolarity参数与通道配置重复 TIM_ICPolarity_Rising代表不反相，TIM_ICPolarity_Falling表示反相 TIM_EncoderMode 值 描述 TIM_EncoderMode_TI1 ((uint16_t)0x0001) Counter counts on TI1FP1 edge depending on TI2FP2 level. TIM_EncoderMode_TI2 ((uint16_t)0x0002) Counter counts on TI2FP2 edge depending on TI1FP1 level. TIM_EncoderMode_TI12 ((uint16_t)0x0003) Counter counts on both TI1FP1 and TI2FP2 edges depending on the level of the other input. ","date":"2024-02-05T00:00:00Z","permalink":"https://blog.detail.eu.org/p/88b59a/","title":"STM32 TIM"},{"content":"STM32 STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器 STM32常应用在嵌入式领域，如智能车、无人机、机器人、无线通信、物联网、工业控制、娱乐电子产品等 STM32功能强大、性能优异、片上资源丰富、功耗低，是一款经典的嵌入式微控制器 ARM ARM既指ARM公司，也指ARM处理器内核 ARM公司是全球领先的半导体知识产权（IP）提供商，全世界超过95%的智能手机和平板电脑都采用ARM架构 ARM公司设计ARM内核，半导体厂商完善内核周边电路并生产芯片 STM32F103C8T6 系列：主流系列STM32F1 内核：ARM Cortex-M3 主频：72MHz RAM：20K（SRAM） ROM：64K（Flash） 供电：2.0~3.6V（标准3.3V） 封装：LQFP48 片上资源/外设 英文缩写 名称 NVIC 嵌套向量中断控制器 SysTick 系统滴答定时器 RCC 复位和时钟控制 GPIO 通用IO口 AFIO 复用IO口 EXTI 外部中断 TIM 定时器 ADC 模数转换器 DMA 直接内存访问 USART 同步/异步串口通信 I2C I2C通信 SPI SPI通信 CAN CAN通信 USB USB通信 RTC 实时时钟 CRC CRC校验 PWR 电源控制 BKP 备份寄存器 IWDG 独立看门狗 WWDG 窗口看门狗 DAC 数模转换器 SDIO SD卡接口 FSMC 可变静态存储控制器 USB OTG USB主机接口 命名规则 引脚定义 引脚重映射 1 2 3 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); 使用引脚重映射时，需要开启AFIO时钟 该代码禁用PA15和PB3的JTAG调试，重映射为TIM2 更多PinRemapConfig的相关内容：STM32F10xxx参考手册（中文）.pdf 页码116/手册8.3\n启动配置 最小系统电路 RCC时钟树 ","date":"2024-02-05T00:00:00Z","permalink":"https://blog.detail.eu.org/p/20f563/","title":"STM32简介"},{"content":"中断 中断：在主程序运行过程中，出现了特定的中断触发条件（中断源），使得CPU暂停当前正在运行的程序，转而去处理中断程序，处理完成后又返回原来被暂停的位置继续运行 中断优先级：当有多个中断源同时申请中断时，CPU会根据中断源的轻重缓急进行裁决，优先响应更加紧急的中断源 中断嵌套：当一个中断程序正在运行时，又有新的更高优先级的中断源申请中断，CPU再次暂停当前中断程序，转而去处理新的中断程序，处理完成后依次进行返回 STM32中断 68个可屏蔽中断通道，包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设 使用NVIC统一管理中断，每个中断通道都拥有16个可编程的优先等级，可对优先级进行分组，进一步设置抢占优先级和响应优先级 NVIC 基本结构 优先级分组 NVIC的中断优先级由优先级寄存器的4位（0~15）决定，这4位可以进行切分，分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套，响应优先级高的可以优先排队，抢占优先级和响应优先级均相同的按中断号排队 分组方式 抢占优先级 响应优先级 宏 值 分组0 0位，取值为0 4位，取值为0~15 NVIC_PriorityGroup_0 ((uint32_t)0x700) 分组1 1位，取值为0~1 3位，取值为0~7 NVIC_PriorityGroup_1 ((uint32_t)0x600) 分组2 2位，取值为0~3 2位，取值为0~3 NVIC_PriorityGroup_2 ((uint32_t)0x500) 分组3 3位，取值为0~7 1位，取值为0~1 NVIC_PriorityGroup_3 ((uint32_t)0x400) 分组4 4位，取值为0~15 0位，取值为0 NVIC_PriorityGroup_4 ((uint32_t)0x300) 代码 1 2 3 4 5 6 7 8 9 10 //NVIC中断优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC配置 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(\u0026amp;NVIC_InitStructure); NVIC_InitTypeDef为NVIC初始化结构体\nNVIC_IRQChannel为中断通道 ( IRQChannel ) NVIC_IRQChannelCmd为通道使能：ENABLE为开启，DISABLE为关闭 NVIC_IRQChannelPreemptionPriority为抢占优先级 NVIC_IRQChannelSubPriority为响应优先级 中断触发后，将会进入中断函数 void xHandler(void) EXTI_GetITStatus函数可获取某个中断标志位 EXTI_ClearITPendingBit函数可清除某个中断标志位（\u0026lt;u\u0026gt;必须 \u0026lt;/u\u0026gt;） 中断通道（以STM32F10X_MD为准） NVIC_IRQChannel 值 描述 WWDG_IRQn 0 Window WatchDog Interrupt PVD_IRQn 1 PVD through EXTI Line detection Interrupt TAMPER_IRQn 2 Tamper Interrupt RTC_IRQn 3 RTC global Interrupt FLASH_IRQn 4 FLASH global Interrupt RCC_IRQn 5 RCC global Interrupt EXTI0_IRQn 6 EXTI Line0 Interrupt EXTI1_IRQn 7 EXTI Line1 Interrupt EXTI2_IRQn 8 EXTI Line2 Interrupt EXTI3_IRQn 9 EXTI Line3 Interrupt EXTI4_IRQn 10 EXTI Line4 Interrupt DMA1_Channel1_IRQn 11 DMA1 Channel 1 global Interrupt DMA1_Channel2_IRQn 12 DMA1 Channel 2 global Interrupt DMA1_Channel3_IRQn 13 DMA1 Channel 3 global Interrupt DMA1_Channel4_IRQn 14 DMA1 Channel 4 global Interrupt DMA1_Channel5_IRQn 15 DMA1 Channel 5 global Interrupt DMA1_Channel6_IRQn 16 DMA1 Channel 6 global Interrupt DMA1_Channel7_IRQn 17 DMA1 Channel 7 global Interrupt ADC1_2_IRQn 18 ADC1 and ADC2 global Interrupt USB_HP_CAN1_TX_IRQn 19 USB Device High Priority or CAN1 TX Interrupts USB_LP_CAN1_RX0_IRQn 20 USB Device Low Priority or CAN1 RX0 Interrupts CAN1_RX1_IRQn 21 CAN1 RX1 Interrupt CAN1_SCE_IRQn 22 CAN1 SCE Interrupt EXTI9_5_IRQn 23 External Line[9:5] Interrupts TIM1_BRK_IRQn 24 TIM1 Break Interrupt TIM1_UP_IRQn 25 TIM1 Update Interrupt TIM1_TRG_COM_IRQn 26 TIM1 Trigger and Commutation Interrupt TIM1_CC_IRQn 27 TIM1 Capture Compare Interrupt TIM2_IRQn 28 TIM2 global Interrupt TIM3_IRQn 29 TIM3 global Interrupt TIM4_IRQn 30 TIM4 global Interrupt I2C1_EV_IRQn 31 I2C1 Event Interrupt I2C1_ER_IRQn 32 I2C1 Error Interrupt I2C2_EV_IRQn 33 I2C2 Event Interrupt I2C2_ER_IRQn 34 I2C2 Error Interrupt SPI1_IRQn 35 SPI1 global Interrupt SPI2_IRQn 36 SPI2 global Interrupt USART1_IRQn 37 USART1 global Interrupt USART2_IRQn 38 USART2 global Interrupt USART3_IRQn 39 USART3 global Interrupt EXTI15_10_IRQn 40 External Line[15:10] Interrupts RTCAlarm_IRQn 41 RTC Alarm through EXTI Line Interrupt USBWakeUp_IRQn 42 USB Device WakeUp from suspend through EXTI Line Interrupt EXTI 基本结构 EXTI（Extern Interrupt）外部中断 EXTI可以监测指定GPIO口的电平信号，当其指定的GPIO口产生电平变化时，EXTI将立即向NVIC发出中断申请，经过NVIC裁决后即可中断CPU主程序，使CPU执行EXTI对应的中断程序 支持的触发方式：上升沿/下降沿/双边沿/软件触发 支持的GPIO口：所有GPIO口，但相同的Pin不能同时触发中断 通道数：16个GPIO_Pin，外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 触发响应方式：中断响应/事件响应 代码 1 2 3 4 5 6 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_Init(\u0026amp;EXTI_InitStructure); EXTI_Line为中断线来源 EXTI_LineCmd为中断线使能：ENABLE为开启，DISABLE为关闭 EXTI_Mode为EXTI的模式 EXTI_Trigger为触发边缘 EXTI_Line 值 描述 EXTI_Line0 ((uint32_t)0x00001) External interrupt line 0 EXTI_Line1 ((uint32_t)0x00002) External interrupt line 1 EXTI_Line2 ((uint32_t)0x00004) External interrupt line 2 EXTI_Line3 ((uint32_t)0x00008) External interrupt line 3 EXTI_Line4 ((uint32_t)0x00010) External interrupt line 4 EXTI_Line5 ((uint32_t)0x00020) External interrupt line 5 EXTI_Line6 ((uint32_t)0x00040) External interrupt line 6 EXTI_Line7 ((uint32_t)0x00080) External interrupt line 7 EXTI_Line8 ((uint32_t)0x00100) External interrupt line 8 EXTI_Line9 ((uint32_t)0x00200) External interrupt line 9 EXTI_Line10 ((uint32_t)0x00400) External interrupt line 10 EXTI_Line11 ((uint32_t)0x00800) External interrupt line 11 EXTI_Line12 ((uint32_t)0x01000) External interrupt line 12 EXTI_Line13 ((uint32_t)0x02000) External interrupt line 13 EXTI_Line14 ((uint32_t)0x04000) External interrupt line 14 EXTI_Line15 ((uint32_t)0x08000) External interrupt line 15 EXTI_Line16 ((uint32_t)0x10000) External interrupt line 16 Connected to the PVD Output EXTI_Line17 ((uint32_t)0x20000) External interrupt line 17 Connected to the RTC Alarm event EXTI_Line18 ((uint32_t)0x40000) External interrupt line 18 Connected to the USB Device/USB OTG FS Wakeup from suspend event EXTI_Line19 ((uint32_t)0x80000) External interrupt line 19 Connected to the Ethernet Wakeup event AFIO 基本结构 AFIO主要用于引脚复用功能的选择和重定义 在STM32中，AFIO主要完成两个任务：复用功能引脚重映射、中断引脚选择 代码 1 2 3 4 //开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //配置AFIO GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); GPIO_PortSourceGPIOx为选择的GPIO_Port GPIO_PinSource为选择的GPIO_Pin 外部中断完整配置流程 配置时钟 GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); AFIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); 配置GPIO GPIO_Init(GPIOB, \u0026amp;GPIO_InitStructure); 配置AFIO GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); 配置EXTI EXTI_Init(\u0026amp;EXTI_InitStructure); 配置NVIC 中断分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 中断配置 NVIC_Init(\u0026amp;NVIC_InitStructure); ","date":"2024-02-02T00:00:00Z","permalink":"https://blog.detail.eu.org/p/fea1c0/","title":"STM32 EXTI"},{"content":"GPIO概述 GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式 引脚电平：0V~3.3V，部分引脚可容忍5V 输出模式下可控制端口输出高低电平，用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 输入模式下可读取端口的高低电平或电压，用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等 GPIO详细介绍 GPIO基本结构 所有GPIO都处于APB2总线上，因此使用GPIO时务必开启APB2总线的时钟。\nIO口结构 图中错误: 图中TTL肖特基触发器应为TTL施密特触发器\nGPIO的不同模式 模式名称 性质 特征 浮空输入 数字输入 可读取引脚电平，若引脚悬空，则电平不确定 上拉输入 数字输入 可读取引脚电平，内部连接上拉电阻，悬空时默认高电平 下拉输入 数字输入 可读取引脚电平，内部连接下拉电阻，悬空时默认低电平 模拟输入 模拟输入 GPIO无效，引脚直接接入内部ADC 开漏输出 数字输出 可输出引脚电平，高电平为高阻态，低电平接VSS 推挽输出 数字输出 可输出引脚电平，高电平接VDD，低电平接VSS 复用开漏输出 数字输出 由片上外设控制，高电平为高阻态，低电平接VSS 复用推挽输出 数字输出 由片上外设控制，高电平接VDD，低电平接VSS 代码实现 结构体 1 2 3 4 5 6 7 8 //开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //配置GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, \u0026amp;GPIO_InitStructure); GPIO_InitTypeDef为GPIO初始化结构体\nGPIO_Mode为GPIO口模式 GPIO_Pin为GPIO引脚编号，和 GPIO_Init的第一个参数共同指定引脚 GPIO_Speed为GPIO工作速度 模式 GPIO_Mode 值 模拟输入 GPIO_Mode_AIN 0x0 浮空输入 GPIO_Mode_IN_FLOATING 0x04 下拉输入 GPIO_Mode_IPD 0x28 上拉输入 GPIO_Mode_IPU 0x48 开漏输出 GPIO_Mode_Out_OD 0x14 推挽输出 GPIO_Mode_Out_PP 0x10 复用开漏输出 GPIO_Mode_AF_OD 0x1C 复用推挽输出 GPIO_Mode_AF_PP 0x18 速度 GPIO_Speed 值 10M GPIO_Speed_10MHz 1 2M GPIO_Speed_2MHz 2 50M GPIO_Speed_50MHz 3 GPIO输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //方法1 GPIO_ResetBits(GPIOA, GPIO_Pin_0); GPIO_SetBits(GPIOA, GPIO_Pin_0); //方法2 GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); //方法3 GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0); GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1); //方法4 uint16_t data = 0x00; GPIO_Write(GPIOA, data); GPIO输入 1 2 3 4 5 6 7 8 9 //方法1 uint8_t data = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); //方法2 uint16_t data = GPIO_ReadInputData(GPIOA); //读取输出寄存器 uint8_t data = GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_0); uint16_t data = GPIO_ReadOutputData(GPIOA); ","date":"2024-01-31T00:00:00Z","permalink":"https://blog.detail.eu.org/p/7ad6f6/","title":"STM32 GPIO"}]