1637 lines
58 KiB
HTML
1637 lines
58 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>SecCodeSmith - Summoning Digital Entities: Advanced STM32 Techniques</title>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
|
<script>hljs.highlightAll();</script>
|
|
<style>
|
|
:root {
|
|
--primary-dark: #0a0a0a;
|
|
--secondary-dark: #1a1a1a;
|
|
--accent-fire: #ff6400;
|
|
--accent-ember: #ff4500;
|
|
--accent-dragon: #8B0000;
|
|
--text-light: #f5f5f5;
|
|
--text-ash: #aaaaaa;
|
|
--glow-effect: 0 0 10px var(--accent-fire), 0 0 20px rgba(255, 100, 0, 0.4);
|
|
--card-bg: #141414;
|
|
--card-border: #2a2a2a;
|
|
--code-bg: #1e1e1e;
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Cinzel', serif;
|
|
}
|
|
|
|
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Fira+Code:wght@400;600&display=swap');
|
|
|
|
body {
|
|
background-color: var(--primary-dark);
|
|
color: var(--text-light);
|
|
background-image: url('background.png');
|
|
background-repeat: repeat;
|
|
background-size: 300px 300px;
|
|
background-position: 0 0;
|
|
position: relative;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
body::before {
|
|
content: "";
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: radial-gradient(circle at 50% 50%, rgba(139, 0, 0, 0.08), rgba(10, 10, 10, 0.4));
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
}
|
|
|
|
header {
|
|
background-color: rgba(10, 10, 10, 0.95);
|
|
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.8);
|
|
position: fixed;
|
|
width: 100%;
|
|
z-index: 1000;
|
|
border-bottom: 1px solid rgba(255, 100, 0, 0.3);
|
|
}
|
|
|
|
.navbar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 15px 5%;
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
position: relative;
|
|
}
|
|
|
|
.logo-icon {
|
|
font-size: 2rem;
|
|
color: var(--accent-fire);
|
|
text-shadow: var(--glow-effect);
|
|
position: relative;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.logo-text {
|
|
font-weight: 700;
|
|
font-size: 1.5rem;
|
|
background: linear-gradient(to right, var(--accent-fire), var(--text-light));
|
|
-webkit-background-clip: text;
|
|
background-clip: text;
|
|
color: transparent;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.nav-links {
|
|
display: flex;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.nav-links a {
|
|
text-decoration: none;
|
|
color: var(--text-light);
|
|
font-size: 1.1rem;
|
|
font-weight: 500;
|
|
position: relative;
|
|
padding: 5px 3px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.nav-links a:hover {
|
|
color: var(--accent-fire);
|
|
}
|
|
|
|
.nav-links a::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 0;
|
|
height: 2px;
|
|
bottom: 0;
|
|
left: 0;
|
|
background: linear-gradient(to right, var(--accent-fire), transparent);
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.nav-links a:hover::after {
|
|
width: 100%;
|
|
}
|
|
|
|
.nav-links a.active {
|
|
color: var(--accent-fire);
|
|
}
|
|
|
|
.nav-links a.active::after {
|
|
width: 100%;
|
|
}
|
|
|
|
.post-container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 120px 20px 60px;
|
|
display: grid;
|
|
grid-template-columns: 1fr 300px;
|
|
gap: 40px;
|
|
}
|
|
|
|
.post-main {
|
|
background-color: var(--card-bg);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--card-border);
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.post-header {
|
|
position: relative;
|
|
}
|
|
|
|
.post-featured-image {
|
|
height: 400px;
|
|
background: url('/api/placeholder/900/400') center/cover no-repeat;
|
|
position: relative;
|
|
}
|
|
|
|
.post-featured-image::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100px;
|
|
background: linear-gradient(to top, var(--card-bg), transparent);
|
|
}
|
|
|
|
.post-category {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 20px;
|
|
background: rgba(139, 0, 0, 0.9);
|
|
color: var(--text-light);
|
|
padding: 5px 15px;
|
|
border-radius: 4px;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
backdrop-filter: blur(5px);
|
|
z-index: 1;
|
|
}
|
|
|
|
.post-info {
|
|
padding: 30px 40px;
|
|
}
|
|
|
|
.post-title {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 20px;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.post-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 20px;
|
|
font-size: 0.9rem;
|
|
color: var(--text-ash);
|
|
margin-bottom: 30px;
|
|
padding-bottom: 20px;
|
|
border-bottom: 1px solid var(--card-border);
|
|
}
|
|
|
|
.meta-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.meta-icon {
|
|
color: var(--accent-fire);
|
|
}
|
|
|
|
.post-content {
|
|
padding: 0 40px 40px;
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
line-height: 1.8;
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
.post-content p {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.post-content h2 {
|
|
font-size: 1.8rem;
|
|
margin: 40px 0 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid var(--card-border);
|
|
font-family: 'Cinzel', serif;
|
|
color: var(--text-light);
|
|
position: relative;
|
|
}
|
|
|
|
.post-content h2::before {
|
|
content: "//";
|
|
margin-right: 10px;
|
|
color: var(--accent-fire);
|
|
font-weight: normal;
|
|
font-family: 'Fira Code', monospace;
|
|
}
|
|
|
|
.post-content h3 {
|
|
font-size: 1.4rem;
|
|
margin: 30px 0 15px;
|
|
font-family: 'Cinzel', serif;
|
|
color: var(--text-light);
|
|
}
|
|
|
|
.post-content ul,
|
|
.post-content ol {
|
|
margin: 0 0 20px 20px;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.post-content li {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.post-content a {
|
|
color: var(--accent-fire);
|
|
text-decoration: none;
|
|
border-bottom: 1px dotted var(--accent-fire);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.post-content a:hover {
|
|
border-bottom-style: solid;
|
|
}
|
|
|
|
.post-content blockquote {
|
|
margin: 30px 0;
|
|
padding: 20px 30px;
|
|
border-left: 4px solid var(--accent-fire);
|
|
background-color: rgba(255, 100, 0, 0.05);
|
|
font-style: italic;
|
|
color: var(--text-ash);
|
|
position: relative;
|
|
}
|
|
|
|
.post-content blockquote::before {
|
|
content: """;
|
|
position: absolute;
|
|
top: 10px;
|
|
left: 10px;
|
|
font-size: 3rem;
|
|
color: rgba(255, 100, 0, 0.2);
|
|
font-family: Georgia, serif;
|
|
}
|
|
|
|
.post-content img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
border-radius: 8px;
|
|
margin: 20px 0;
|
|
display: block;
|
|
}
|
|
|
|
.post-content pre {
|
|
margin: 20px 0;
|
|
border-radius: 8px;
|
|
overflow-x: auto;
|
|
background-color: var(--code-bg);
|
|
font-family: 'Fira Code', monospace;
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|
border: 1px solid var(--card-border);
|
|
}
|
|
|
|
.post-content code {
|
|
font-family: 'Fira Code', monospace;
|
|
font-size: 0.9rem;
|
|
padding: 0 4px;
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
border-radius: 3px;
|
|
color: var(--text-light);
|
|
}
|
|
|
|
.post-content pre code {
|
|
padding: 0;
|
|
background-color: transparent;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.post-content .highlight {
|
|
margin: 30px 0;
|
|
position: relative;
|
|
}
|
|
|
|
.post-content .highlight::before {
|
|
content: attr(data-language);
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
background-color: var(--accent-fire);
|
|
color: var(--text-light);
|
|
padding: 2px 10px;
|
|
font-size: 0.7rem;
|
|
border-radius: 0 8px 0 8px;
|
|
font-family: 'Fira Code', monospace;
|
|
text-transform: uppercase;
|
|
z-index: 1;
|
|
}
|
|
|
|
.code-title {
|
|
background-color: rgba(255, 100, 0, 0.1);
|
|
padding: 10px 15px;
|
|
border-radius: 8px 8px 0 0;
|
|
font-family: 'Fira Code', monospace;
|
|
font-size: 0.9rem;
|
|
color: var(--accent-fire);
|
|
border-bottom: 1px solid rgba(255, 100, 0, 0.2);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.post-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
margin-top: 40px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid var(--card-border);
|
|
}
|
|
|
|
.post-tag {
|
|
background-color: rgba(255, 100, 0, 0.1);
|
|
color: var(--accent-fire);
|
|
padding: 5px 12px;
|
|
border-radius: 4px;
|
|
font-size: 0.8rem;
|
|
font-weight: 500;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.post-tag:hover {
|
|
background-color: rgba(255, 100, 0, 0.2);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.share-section {
|
|
margin-top: 40px;
|
|
padding-top: 20px;
|
|
border-top: 1px solid var(--card-border);
|
|
}
|
|
|
|
.share-title {
|
|
font-size: 1.2rem;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.share-buttons {
|
|
display: flex;
|
|
gap: 15px;
|
|
}
|
|
|
|
.share-button {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--text-ash);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.share-button:hover {
|
|
background-color: var(--accent-fire);
|
|
color: var(--text-light);
|
|
transform: translateY(-3px);
|
|
}
|
|
|
|
.author-section {
|
|
margin-top: 40px;
|
|
padding: 30px;
|
|
background-color: rgba(255, 255, 255, 0.02);
|
|
border-radius: 8px;
|
|
display: flex;
|
|
gap: 20px;
|
|
align-items: center;
|
|
}
|
|
|
|
.author-avatar {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
border: 3px solid rgba(255, 100, 0, 0.3);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.author-avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.author-info {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.author-name {
|
|
font-size: 1.2rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.author-bio {
|
|
color: var(--text-ash);
|
|
font-size: 0.9rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.author-social {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.author-social a {
|
|
color: var(--text-ash);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.author-social a:hover {
|
|
color: var(--accent-fire);
|
|
}
|
|
|
|
.comments-section {
|
|
margin-top: 60px;
|
|
}
|
|
|
|
.comments-title {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 30px;
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.comments-title::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 2px;
|
|
background: var(--accent-fire);
|
|
bottom: -10px;
|
|
left: 0;
|
|
}
|
|
|
|
.comment {
|
|
padding: 20px 0;
|
|
border-bottom: 1px solid var(--card-border);
|
|
display: flex;
|
|
gap: 20px;
|
|
}
|
|
|
|
.comment-avatar {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.comment-avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.comment-content {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.comment-author {
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.comment-date {
|
|
font-size: 0.8rem;
|
|
color: var(--text-ash);
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.comment-text {
|
|
color: #e0e0e0;
|
|
line-height: 1.6;
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
}
|
|
|
|
.comment-reply {
|
|
margin-top: 10px;
|
|
color: var(--accent-fire);
|
|
text-decoration: none;
|
|
font-size: 0.9rem;
|
|
display: inline-block;
|
|
}
|
|
|
|
.comment-form {
|
|
margin-top: 40px;
|
|
}
|
|
|
|
.form-title {
|
|
font-size: 1.3rem;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
background-color: rgba(255, 255, 255, 0.05);
|
|
border: 1px solid var(--card-border);
|
|
border-radius: 4px;
|
|
padding: 12px 15px;
|
|
color: var(--text-light);
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
resize: vertical;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.form-control:focus {
|
|
outline: none;
|
|
border-color: var(--accent-fire);
|
|
box-shadow: 0 0 0 2px rgba(255, 100, 0, 0.2);
|
|
}
|
|
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 20px;
|
|
}
|
|
|
|
.submit-button {
|
|
background-color: var(--accent-fire);
|
|
color: var(--text-light);
|
|
border: none;
|
|
border-radius: 4px;
|
|
padding: 12px 25px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
font-family: 'Cinzel', serif;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.submit-button:hover {
|
|
background-color: var(--accent-dragon);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.post-sidebar {
|
|
position: sticky;
|
|
top: 120px;
|
|
align-self: start;
|
|
}
|
|
|
|
.sidebar-widget {
|
|
background-color: var(--card-bg);
|
|
border-radius: 8px;
|
|
padding: 25px;
|
|
margin-bottom: 30px;
|
|
border: 1px solid var(--card-border);
|
|
}
|
|
|
|
.widget-title {
|
|
font-size: 1.2rem;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
position: relative;
|
|
}
|
|
|
|
.widget-title::after {
|
|
content: "";
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 2px;
|
|
background: var(--accent-fire);
|
|
bottom: -1px;
|
|
left: 0;
|
|
}
|
|
|
|
.toc-list {
|
|
list-style: none;
|
|
}
|
|
|
|
.toc-item {
|
|
margin-bottom: 12px;
|
|
padding-left: 15px;
|
|
position: relative;
|
|
}
|
|
|
|
.toc-item::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
top: 10px;
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background-color: var(--accent-fire);
|
|
}
|
|
|
|
.toc-link {
|
|
color: var(--text-ash);
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
font-size: 0.9rem;
|
|
line-height: 1.5;
|
|
display: block;
|
|
}
|
|
|
|
.toc-link:hover {
|
|
color: var(--accent-fire);
|
|
transform: translateX(5px);
|
|
}
|
|
|
|
.toc-subitem {
|
|
margin-top: 8px;
|
|
padding-left: 15px;
|
|
}
|
|
|
|
.related-posts-list {
|
|
list-style: none;
|
|
}
|
|
|
|
.related-post-item {
|
|
margin-bottom: 15px;
|
|
padding-bottom: 15px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
.related-post-item:last-child {
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.related-post-link {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
text-decoration: none;
|
|
color: var(--text-ash);
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.related-post-link:hover {
|
|
color: var(--accent-fire);
|
|
}
|
|
|
|
.related-post-thumb {
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 4px;
|
|
background-size: cover;
|
|
background-position: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.related-post-info {
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.related-post-title {
|
|
font-size: 0.9rem;
|
|
line-height: 1.4;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.related-post-date {
|
|
font-size: 0.8rem;
|
|
color: rgba(255, 255, 255, 0.5);
|
|
}
|
|
|
|
.footer {
|
|
background-color: var(--primary-dark);
|
|
padding: 50px 5% 30px;
|
|
text-align: center;
|
|
position: relative;
|
|
border-top: 1px solid rgba(255, 100, 0, 0.2);
|
|
margin-top: 80px;
|
|
}
|
|
|
|
.footer-content {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.social-links {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.social-icon {
|
|
font-size: 1.5rem;
|
|
color: var(--text-ash);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.social-icon:hover {
|
|
color: var(--accent-fire);
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
.copyright {
|
|
color: var(--text-ash);
|
|
font-size: 0.9rem;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.binary-line {
|
|
position: absolute;
|
|
font-family: 'Fira Code', monospace;
|
|
color: var(--accent-fire);
|
|
font-size: 0.8rem;
|
|
opacity: 0.15;
|
|
pointer-events: none;
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.post-container {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.post-sidebar {
|
|
position: static;
|
|
order: -1;
|
|
}
|
|
|
|
.post-featured-image {
|
|
height: 300px;
|
|
}
|
|
|
|
.post-title {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.post-info, .post-content {
|
|
padding: 20px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.navbar {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.nav-links {
|
|
width: 100%;
|
|
justify-content: space-around;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.post-featured-image {
|
|
height: 250px;
|
|
}
|
|
|
|
.post-title {
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.form-row {
|
|
grid-template-columns: 1fr;
|
|
gap: 15px;
|
|
}
|
|
|
|
.author-section {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.author-social {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.nav-links {
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.post-featured-image {
|
|
height: 200px;
|
|
}
|
|
|
|
.post-title {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.post-meta {
|
|
gap: 10px;
|
|
}
|
|
|
|
.comment {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.comment-avatar {
|
|
margin-bottom: 10px;
|
|
}
|
|
}
|
|
|
|
/* Custom scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: var(--primary-dark);
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--card-border);
|
|
border-radius: 5px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: var(--accent-fire);
|
|
}
|
|
|
|
/* Code line numbers */
|
|
.hljs-ln-numbers {
|
|
user-select: none;
|
|
text-align: center;
|
|
color: rgba(255, 255, 255, 0.3);
|
|
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
|
vertical-align: top;
|
|
padding-right: 10px !important;
|
|
}
|
|
|
|
.hljs-ln-code {
|
|
padding-left: 10px !important;
|
|
}
|
|
|
|
/* Tooltips */
|
|
.tooltip {
|
|
position: relative;
|
|
display: inline-block;
|
|
color: var(--accent-fire);
|
|
text-decoration: underline;
|
|
text-decoration-style: dotted;
|
|
cursor: help;
|
|
}
|
|
|
|
.tooltip .tooltip-text {
|
|
visibility: hidden;
|
|
width: 200px;
|
|
background-color: var(--card-bg);
|
|
color: var(--text-light);
|
|
text-align: center;
|
|
border-radius: 6px;
|
|
padding: 10px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
bottom: 125%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
border: 1px solid var(--card-border);
|
|
font-size: 0.8rem;
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.tooltip .tooltip-text::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 50%;
|
|
margin-left: -5px;
|
|
border-width: 5px;
|
|
border-style: solid;
|
|
border-color: var(--card-border) transparent transparent transparent;
|
|
}
|
|
|
|
.tooltip:hover .tooltip-text {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Header and Navigation -->
|
|
<header>
|
|
<nav class="navbar">
|
|
<div class="logo">
|
|
<div class="logo-icon">
|
|
<i class="fas fa-hammer"></i>
|
|
<i class="fas fa-dragon" style="font-size: 1.2rem; position: absolute; top: 0; right: -12px; color: var(--accent-dragon);"></i>
|
|
</div>
|
|
<div class="logo-text">SecCodeSmith</div>
|
|
</div>
|
|
<div class="nav-links">
|
|
<a href="index.html">Home</a>
|
|
<a href="blog.html" class="active">Blog</a>
|
|
<a href="about.html">About</a>
|
|
<a href="projects.html">Projects</a>
|
|
<a href="contact.html">Contact</a>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<!-- Main Post Content -->
|
|
<div class="post-container">
|
|
<main class="post-main">
|
|
<article>
|
|
<!-- Post Header -->
|
|
<div class="post-header">
|
|
<div class="post-featured-image"></div>
|
|
<div class="post-category">Embedded Systems</div>
|
|
</div>
|
|
|
|
<!-- Post Info -->
|
|
<div class="post-info">
|
|
<h1 class="post-title">Summoning Digital Entities: Advanced STM32 Techniques for IoT Communication</h1>
|
|
<div class="post-meta">
|
|
<div class="meta-item">
|
|
<i class="fas fa-calendar-alt meta-icon"></i>
|
|
<span>May 5, 2025</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<i class="fas fa-user meta-icon"></i>
|
|
<span>SecCodeSmith</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<i class="fas fa-clock meta-icon"></i>
|
|
<span>15 min read</span>
|
|
</div>
|
|
<div class="meta-item">
|
|
<i class="fas fa-comments meta-icon"></i>
|
|
<span>12 Comments</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Post Content -->
|
|
<div class="post-content">
|
|
<p>In the digital forge where hardware meets software, the STM32 microcontroller family stands as a powerful conduit for summoning digital entities into our physical realm. Today, we shall delve into advanced techniques for establishing robust communication between these entities through the mystical arts of embedded systems programming.</p>
|
|
|
|
<p>The realm of IoT demands reliable, efficient communication protocols that can withstand the chaotic nature of real-world environments. Through careful implementation and optimization of the STM32's communication peripherals, we can craft resilient connections that bridge the gap between digital and physical domains.</p>
|
|
|
|
<h2>The Three Elemental Protocols: I2C, SPI, and UART</h2>
|
|
|
|
<p>Before we forge ahead into advanced territory, let us briefly recall the elemental forces at our disposal. Each protocol serves as a channel through which our digital entities communicate, each with its unique strengths and limitations:</p>
|
|
|
|
<ul>
|
|
<li><strong>I2C (Inter-Integrated Circuit)</strong> - A two-wire interface requiring minimal physical connections, ideal for communication with multiple peripheral devices over short distances.</li>
|
|
<li><strong>SPI (Serial Peripheral Interface)</strong> - A faster, full-duplex protocol with dedicated select lines for each peripheral, allowing simultaneous transmission and reception of data.</li>
|
|
<li><strong>UART (Universal Asynchronous Receiver-Transmitter)</strong> - A simple two-wire asynchronous interface, perfect for direct device-to-device communication without a shared clock.</li>
|
|
</ul>
|
|
|
|
<p>Now, let us transcend the basics and explore the deeper mysteries of these protocols as implemented on the STM32 platform.</p>
|
|
|
|
<h2>Optimizing I2C for Reliable Field Operations</h2>
|
|
|
|
<p>The I2C protocol, while powerful in its simplicity, can encounter several challenges in real-world deployments. Addressing these challenges requires both hardware considerations and software techniques.</p>
|
|
|
|
<h3>Hardware Incantations: Pull-up Resistor Selection</h3>
|
|
|
|
<p>The choice of pull-up resistors on the SCL and SDA lines is crucial for stable communication. Too high a resistance leads to slow signal rise times, while too low a value increases power consumption and may overload outputs.</p>
|
|
|
|
<p>For most applications, values between 2.2kΩ and 10kΩ work well, but the optimal value depends on the capacitance of your bus and the desired speed:</p>
|
|
|
|
<ul>
|
|
<li>Standard Mode (100 kHz) - 4.7kΩ to 10kΩ</li>
|
|
<li>Fast Mode (400 kHz) - 2.2kΩ to 4.7kΩ</li>
|
|
<li>Fast Mode Plus (1 MHz) - 1kΩ to 2.2kΩ</li>
|
|
</ul>
|
|
|
|
<h3>Software Rituals: Handling Bus Errors</h3>
|
|
|
|
<p>I2C buses can sometimes get locked in an error state due to noise or improper reset sequences. Implementing a recovery mechanism can prevent your system from requiring a full power cycle.</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
i2c_recovery.c
|
|
</div>
|
|
<pre><code class="language-c">/* This ritual manually toggles the clock line to free a stuck bus */
|
|
bool I2C_ForceBusRecovery(GPIO_TypeDef* scl_port, uint16_t scl_pin,
|
|
GPIO_TypeDef* sda_port, uint16_t sda_pin)
|
|
{
|
|
// Disable the I2C peripheral
|
|
I2C1->CR1 &= ~I2C_CR1_PE;
|
|
|
|
// Configure SCL and SDA as open-drain outputs
|
|
GPIO_InitTypeDef gpio;
|
|
gpio.Mode = GPIO_MODE_OUTPUT_OD;
|
|
gpio.Pull = GPIO_NOPULL;
|
|
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
|
|
|
|
gpio.Pin = scl_pin;
|
|
HAL_GPIO_Init(scl_port, &gpio);
|
|
|
|
gpio.Pin = sda_pin;
|
|
HAL_GPIO_Init(sda_port, &gpio);
|
|
|
|
// Ensure SDA is high
|
|
HAL_GPIO_WritePin(sda_port, sda_pin, GPIO_PIN_SET);
|
|
|
|
// Toggle SCL until SDA goes high
|
|
for(int i = 0; i < 9; i++) {
|
|
// Clock high, delay
|
|
HAL_GPIO_WritePin(scl_port, scl_pin, GPIO_PIN_SET);
|
|
HAL_Delay(1);
|
|
|
|
// Clock low, delay
|
|
HAL_GPIO_WritePin(scl_port, scl_pin, GPIO_PIN_RESET);
|
|
HAL_Delay(1);
|
|
}
|
|
|
|
// Generate a STOP condition
|
|
HAL_GPIO_WritePin(sda_port, sda_pin, GPIO_PIN_RESET);
|
|
HAL_Delay(1);
|
|
HAL_GPIO_WritePin(scl_port, scl_pin, GPIO_PIN_SET);
|
|
HAL_Delay(1);
|
|
HAL_GPIO_WritePin(sda_port, sda_pin, GPIO_PIN_SET);
|
|
HAL_Delay(1);
|
|
|
|
// Reconfigure pins for I2C peripheral
|
|
gpio.Mode = GPIO_MODE_AF_OD;
|
|
gpio.Alternate = GPIO_AF4_I2C1;
|
|
|
|
gpio.Pin = scl_pin;
|
|
HAL_GPIO_Init(scl_port, &gpio);
|
|
|
|
gpio.Pin = sda_pin;
|
|
HAL_GPIO_Init(sda_port, &gpio);
|
|
|
|
// Re-enable I2C peripheral
|
|
I2C1->CR1 |= I2C_CR1_PE;
|
|
|
|
return HAL_GPIO_ReadPin(sda_port, sda_pin) == GPIO_PIN_SET;
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<p>This recovery function manually toggles the SCL line to free any stuck peripheral devices, then generates a proper STOP condition to reset the bus state.</p>
|
|
|
|
<h2>Harnessing the Speed of SPI with DMA</h2>
|
|
|
|
<p>SPI offers superior speed compared to I2C, but to truly unleash its potential, we must employ Direct Memory Access (DMA) to offload the CPU during data transfers.</p>
|
|
|
|
<h3>Configuration for Maximum Throughput</h3>
|
|
|
|
<p>Setting up SPI with DMA involves configuring both the SPI peripheral and associated DMA channels:</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
spi_dma_config.c
|
|
</div>
|
|
<pre><code class="language-c">/* SPI configuration with DMA for high-speed data transfer */
|
|
void SPI_Init_DMA(void)
|
|
{
|
|
// Enable clocks
|
|
__HAL_RCC_SPI1_CLK_ENABLE();
|
|
__HAL_RCC_DMA2_CLK_ENABLE();
|
|
|
|
// Configure SPI
|
|
hspi1.Instance = SPI1;
|
|
hspi1.Init.Mode = SPI_MODE_MASTER;
|
|
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
|
|
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
|
|
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
|
|
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
|
|
hspi1.Init.NSS = SPI_NSS_SOFT;
|
|
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // Tune for your clock frequency
|
|
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
|
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
|
|
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
|
HAL_SPI_Init(&hspi1);
|
|
|
|
// Configure DMA for TX
|
|
hdma_spi1_tx.Instance = DMA2_Stream3;
|
|
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
|
|
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
|
|
hdma_spi1_tx.Init.PeriphInc = DMA_PERIPH_INC_DISABLE;
|
|
hdma_spi1_tx.Init.MemInc = DMA_MEMORY_INC_ENABLE;
|
|
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
|
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
|
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
|
|
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
|
|
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
|
HAL_DMA_Init(&hdma_spi1_tx);
|
|
|
|
// Link DMA to SPI
|
|
__HAL_LINKDMA(&hspi1, hdmatx, hdma_spi1_tx);
|
|
|
|
// Configure DMA for RX (similar to TX)
|
|
hdma_spi1_rx.Instance = DMA2_Stream0;
|
|
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
|
|
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
|
// ... similar configuration as TX
|
|
HAL_DMA_Init(&hdma_spi1_rx);
|
|
|
|
// Link DMA to SPI
|
|
__HAL_LINKDMA(&hspi1, hdmarx, hdma_spi1_rx);
|
|
|
|
// Configure NVIC for DMA
|
|
HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 1, 0);
|
|
HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
|
|
|
|
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 0);
|
|
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<h3>Efficient Data Transfer for Sensor Arrays</h3>
|
|
|
|
<p>When working with multiple sensor readings, we can create a transfer function that efficiently handles large batches of data:</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
spi_bulk_transfer.c
|
|
</div>
|
|
<pre><code class="language-c">/* Efficient transfer of multiple sensor readings via SPI */
|
|
void SPI_TransferSensorData(uint8_t* tx_buffer, uint8_t* rx_buffer, uint16_t size)
|
|
{
|
|
// Select the peripheral
|
|
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
|
|
|
|
// Start DMA transfer (non-blocking)
|
|
HAL_SPI_TransmitReceive_DMA(&hspi1, tx_buffer, rx_buffer, size);
|
|
|
|
// The following code can be executed while DMA handles the transfer
|
|
// Process other tasks, update statuses, etc.
|
|
|
|
// Wait for completion if needed
|
|
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
|
|
|
|
// Deselect the peripheral
|
|
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<blockquote>
|
|
<p>When working with SPI devices at high speeds, ensure proper signal integrity through appropriate PCB layout techniques. Keep traces short, use ground planes, and consider impedance matching for long connections.</p>
|
|
</blockquote>
|
|
|
|
<h2>UART and the Art of Reliable Communication</h2>
|
|
|
|
<p>UART communication forms the backbone of many IoT systems, particularly when connecting to gateways or cellular modules. Enhancing its reliability requires careful error handling and buffer management.</p>
|
|
|
|
<h3>Implementing a Circular Buffer for UART Reception</h3>
|
|
|
|
<p>A circular buffer allows for continuous reception of data without interruption, even when processing is temporarily busy:</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
uart_circular_buffer.c
|
|
</div>
|
|
<pre><code class="language-c">/* Circular buffer implementation for UART reception */
|
|
#define UART_BUFFER_SIZE 256
|
|
|
|
typedef struct {
|
|
uint8_t buffer[UART_BUFFER_SIZE];
|
|
volatile uint16_t head;
|
|
volatile uint16_t tail;
|
|
} CircularBuffer;
|
|
|
|
CircularBuffer uart_rx_buffer = {0};
|
|
|
|
// Initialize buffer
|
|
void CircularBuffer_Init(CircularBuffer* cb) {
|
|
cb->head = 0;
|
|
cb->tail = 0;
|
|
}
|
|
|
|
// Add byte to buffer
|
|
bool CircularBuffer_Write(CircularBuffer* cb, uint8_t data) {
|
|
uint16_t next_head = (cb->head + 1) % UART_BUFFER_SIZE;
|
|
|
|
if (next_head == cb->tail) {
|
|
return false; // Buffer full
|
|
}
|
|
|
|
cb->buffer[cb->head] = data;
|
|
cb->head = next_head;
|
|
return true;
|
|
}
|
|
|
|
// Get byte from buffer
|
|
bool CircularBuffer_Read(CircularBuffer* cb, uint8_t* data) {
|
|
if (cb->head == cb->tail) {
|
|
return false; // Buffer empty
|
|
}
|
|
|
|
*data = cb->buffer[cb->tail];
|
|
cb->tail = (cb->tail + 1) % UART_BUFFER_SIZE;
|
|
return true;
|
|
}
|
|
|
|
// ISR for UART reception
|
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
|
|
static uint8_t rx_byte;
|
|
|
|
if (huart->Instance == USART2) {
|
|
// Add received byte to buffer
|
|
CircularBuffer_Write(&uart_rx_buffer, rx_byte);
|
|
|
|
// Set up for next byte reception
|
|
HAL_UART_Receive_IT(huart, &rx_byte, 1);
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<h3>Implementing a Message Protocol with Error Detection</h3>
|
|
|
|
<p>For robust UART communication, implement a simple message protocol with framing and CRC checking:</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
uart_message_protocol.c
|
|
</div>
|
|
<pre><code class="language-c">/* Message protocol implementation with CRC-16 error detection */
|
|
#define MSG_START_DELIMITER 0x7E
|
|
#define MSG_END_DELIMITER 0x7F
|
|
#define MSG_ESCAPE 0x7D
|
|
#define MSG_MAX_LENGTH 128
|
|
|
|
typedef struct {
|
|
uint8_t buffer[MSG_MAX_LENGTH];
|
|
uint16_t length;
|
|
bool in_frame;
|
|
bool escape_next;
|
|
uint16_t crc;
|
|
} MessageParser;
|
|
|
|
// CRC-16 calculation
|
|
uint16_t CRC16_Calculate(uint8_t* data, uint16_t length) {
|
|
uint16_t crc = 0xFFFF;
|
|
|
|
for (uint16_t i = 0; i < length; i++) {
|
|
crc ^= (uint16_t)data[i];
|
|
|
|
for (uint8_t j = 0; j < 8; j++) {
|
|
if (crc & 0x0001) {
|
|
crc = (crc >> 1) ^ 0xA001; // 0xA001 is the reflection of the polynomial x^16 + x^15 + x^2 + 1
|
|
} else {
|
|
crc = crc >> 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
// Process incoming byte
|
|
void MessageParser_ProcessByte(MessageParser* parser, uint8_t byte, void (*message_handler)(uint8_t*, uint16_t)) {
|
|
if (byte == MSG_START_DELIMITER && !parser->in_frame) {
|
|
// Start of new message
|
|
parser->in_frame = true;
|
|
parser->length = 0;
|
|
parser->escape_next = false;
|
|
return;
|
|
}
|
|
|
|
if (!parser->in_frame) {
|
|
return; // Ignore bytes outside of frame
|
|
}
|
|
|
|
if (parser->escape_next) {
|
|
// Previous byte was escape character
|
|
parser->escape_next = false;
|
|
parser->buffer[parser->length++] = byte ^ 0x20; // XOR with 0x20 to get original value
|
|
} else if (byte == MSG_ESCAPE) {
|
|
// Escape character
|
|
parser->escape_next = true;
|
|
} else if (byte == MSG_END_DELIMITER) {
|
|
// End of message, process it
|
|
if (parser->length >= 2) {
|
|
// Extract CRC (last 2 bytes)
|
|
uint16_t received_crc = (parser->buffer[parser->length - 2] << 8) | parser->buffer[parser->length - 1];
|
|
uint16_t calculated_crc = CRC16_Calculate(parser->buffer, parser->length - 2);
|
|
|
|
if (received_crc == calculated_crc) {
|
|
// Valid message, call handler
|
|
message_handler(parser->buffer, parser->length - 2);
|
|
} else {
|
|
// CRC error, message corrupted
|
|
// Handle error (log, retry, etc.)
|
|
}
|
|
}
|
|
|
|
// Reset for next message
|
|
parser->in_frame = false;
|
|
} else {
|
|
// Regular data byte
|
|
if (parser->length < MSG_MAX_LENGTH) {
|
|
parser->buffer[parser->length++] = byte;
|
|
} else {
|
|
// Buffer overflow
|
|
parser->in_frame = false; // Abort current frame
|
|
// Handle error (log, reset, etc.)
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<p>This protocol provides robust framing with start/end delimiters, byte escaping for transparent data transfer, and CRC-16 error detection to ensure data integrity.</p>
|
|
|
|
<h2>Power Management and Communication Optimization</h2>
|
|
|
|
<p>For IoT devices operating on battery power, optimizing communication for power efficiency is critical. Several techniques can significantly extend operational life:</p>
|
|
|
|
<h3>Low-Power Communication Patterns</h3>
|
|
|
|
<ul>
|
|
<li><strong>Batch Transmissions</strong> - Collect and transmit data in batches rather than continuously.</li>
|
|
<li><strong>Adaptive Sampling</strong> - Adjust sampling rates based on detected activity or variance in readings.</li>
|
|
<li><strong>Sleep Modes</strong> - Put peripherals into sleep mode when not in use, waking them only when needed.</li>
|
|
</ul>
|
|
|
|
<p>The following example demonstrates a power-optimized sensing routine using the STM32's low-power modes:</p>
|
|
|
|
<div class="highlight" data-language="C">
|
|
<div class="code-title">
|
|
<i class="fas fa-code"></i>
|
|
low_power_sensing.c
|
|
</div>
|
|
<pre><code class="language-c">/* Power-optimized sensing routine */
|
|
void LowPowerSensingCycle(void) {
|
|
static uint8_t buffer[SAMPLE_BUFFER_SIZE];
|
|
static uint16_t sample_count = 0;
|
|
|
|
// Wake up I2C peripheral
|
|
I2C_WakeUp(&hi2c1);
|
|
|
|
// Take sensor reading
|
|
if (ReadSensorData(&hi2c1, buffer + sample_count) == HAL_OK) {
|
|
sample_count += SENSOR_DATA_SIZE;
|
|
|
|
// If buffer full or significant event detected, transmit data
|
|
if (sample_count >= SAMPLE_BUFFER_SIZE || DetectSignificantEvent(buffer, sample_count)) {
|
|
// Wake up communication module
|
|
SPI_WakeUp(&hspi1);
|
|
|
|
// Transmit data
|
|
TransmitSensorData(&hspi1, buffer, sample_count);
|
|
|
|
// Put communication module back to sleep
|
|
SPI_EnterLowPower(&hspi1);
|
|
|
|
// Reset buffer
|
|
sample_count = 0;
|
|
}
|
|
}
|
|
|
|
// Put I2C peripheral back to sleep
|
|
I2C_EnterLowPower(&hi2c1);
|
|
|
|
// Calculate time until next sample
|
|
uint32_t sleep_time = CalculateNextSampleTime();
|
|
|
|
// Enter stop mode with RTC wakeup
|
|
EnterStopMode(sleep_time);
|
|
}</code></pre>
|
|
</div>
|
|
|
|
<p>By judiciously managing peripheral power states and transmission frequency, we can achieve a balance between responsiveness and power consumption that extends device life from days to months or even years.</p>
|
|
|
|
<h2>Conclusion: The Art of Digital Communication</h2>
|
|
|
|
<p>Mastering the communication capabilities of the STM32 platform allows us to create IoT devices that reliably bridge the digital and physical realms. By understanding the strengths and limitations of each protocol, implementing robust error handling, and optimizing for power efficiency, we can forge connections that withstand the challenges of real-world deployment.</p>
|
|
|
|
<p>The techniques presented here form just the beginning of your journey into the arcane arts of embedded communication. As you implement these approaches in your own projects, you'll develop an intuitive understanding of when to apply each technique and how to adapt them to your specific requirements.</p>
|
|
|
|
<p>In future articles, we'll explore more advanced topics such as wireless communication protocols, secure boot and firmware updates, and techniques for scalable IoT device management. Until then, may your digital entities communicate without interference, and your embedded systems run true.</p>
|
|
|
|
<div class="post-tags">
|
|
<span class="post-tag">STM32</span>
|
|
<span class="post-tag">Embedded</span>
|
|
<span class="post-tag">IoT</span>
|
|
<span class="post-tag">I2C</span>
|
|
<span class="post-tag">SPI</span>
|
|
<span class="post-tag">UART</span>
|
|
<span class="post-tag">DMA</span>
|
|
<span class="post-tag">Low-Power</span>
|
|
</div>
|
|
|
|
<!-- Share Section -->
|
|
<div class="share-section">
|
|
<h3 class="share-title">Share this arcane knowledge</h3>
|
|
<div class="share-buttons">
|
|
<a href="#" class="share-button"><i class="fab fa-twitter"></i></a>
|
|
<a href="#" class="share-button"><i class="fab fa-facebook-f"></i></a>
|
|
<a href="#" class="share-button"><i class="fab fa-linkedin-in"></i></a>
|
|
<a href="#" class="share-button"><i class="fab fa-reddit-alien"></i></a>
|
|
<a href="#" class="share-button"><i class="far fa-envelope"></i></a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Author Info -->
|
|
<div class="author-section">
|
|
<div class="author-avatar">
|
|
<img src="/api/placeholder/100/100" alt="SecCodeSmith">
|
|
</div>
|
|
<div class="author-info">
|
|
<h3 class="author-name">SecCodeSmith</h3>
|
|
<p class="author-bio">Digital blacksmith forging embedded systems and IoT solutions in the fires of innovation. Specializes in low-level firmware, communication protocols, and hardware-software integration for resource-constrained devices.</p>
|
|
<div class="author-social">
|
|
<a href="#"><i class="fab fa-github"></i></a>
|
|
<a href="#"><i class="fab fa-twitter"></i></a>
|
|
<a href="#"><i class="fab fa-linkedin-in"></i></a>
|
|
<a href="#"><i class="fab fa-stack-overflow"></i></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comments Section -->
|
|
<div class="comments-section">
|
|
<h3 class="comments-title">Discussions (12)</h3>
|
|
|
|
<!-- Comment 1 -->
|
|
<div class="comment">
|
|
<div class="comment-avatar">
|
|
<img src="/api/placeholder/50/50" alt="User">
|
|
</div>
|
|
<div class="comment-content">
|
|
<h4 class="comment-author">Michael Chen</h4>
|
|
<div class="comment-date">May 5, 2025 at 15:42</div>
|
|
<p class="comment-text">Excellent article! I've been struggling with I2C bus lockups in my weather station project. The recovery function you provided solved the issue. Have you considered adding a timeout mechanism to the SPI transfers for error detection?</p>
|
|
<a href="#" class="comment-reply">Reply</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comment 2 -->
|
|
<div class="comment">
|
|
<div class="comment-avatar">
|
|
<img src="/api/placeholder/50/50" alt="User">
|
|
</div>
|
|
<div class="comment-content">
|
|
<h4 class="comment-author">SecCodeSmith</h4>
|
|
<div class="comment-date">May 5, 2025 at 16:10</div>
|
|
<p class="comment-text">@Michael - Good point about the timeout! For SPI transfers, you can implement a timeout using either the HAL Timeout parameter or a hardware timer. For maximum reliability, I prefer using the hardware timer approach as it continues to function even if the main code gets stuck. I'll cover this in a future article on error handling.</p>
|
|
<a href="#" class="comment-reply">Reply</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comment 3 -->
|
|
<div class="comment">
|
|
<div class="comment-avatar">
|
|
<img src="/api/placeholder/50/50" alt="User">
|
|
</div>
|
|
<div class="comment-content">
|
|
<h4 class="comment-author">Elena Petrova</h4>
|
|
<div class="comment-date">May 6, 2025 at 09:23</div>
|
|
<p class="comment-text">The circular buffer implementation for UART is very useful, but I noticed it doesn't handle the case where an interrupt occurs while processing data from the buffer. Have you considered adding a critical section to prevent race conditions?</p>
|
|
<a href="#" class="comment-reply">Reply</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Comment Form -->
|
|
<div class="comment-form">
|
|
<h3 class="form-title">Leave your mark</h3>
|
|
<form>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<input type="text" class="form-control" placeholder="Your Name">
|
|
</div>
|
|
<div class="form-group">
|
|
<input type="email" class="form-control" placeholder="Your Email">
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<textarea class="form-control" rows="5" placeholder="Your Comment"></textarea>
|
|
</div>
|
|
<button type="submit" class="submit-button">Post Comment</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</main>
|
|
|
|
<aside class="post-sidebar">
|
|
<!-- Table of Contents -->
|
|
<div class="sidebar-widget">
|
|
<h3 class="widget-title">Table of Contents</h3>
|
|
<ul class="toc-list">
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">Introduction</a>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">The Three Elemental Protocols</a>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">Optimizing I2C for Reliable Field Operations</a>
|
|
<ul class="toc-list">
|
|
<li class="toc-subitem">
|
|
<a href="#" class="toc-link">Hardware Incantations</a>
|
|
</li>
|
|
<li class="toc-subitem">
|
|
<a href="#" class="toc-link">Software Rituals</a>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">Harnessing the Speed of SPI with DMA</a>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">UART and the Art of Reliable Communication</a>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">Power Management and Optimization</a>
|
|
</li>
|
|
<li class="toc-item">
|
|
<a href="#" class="toc-link">Conclusion</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Related Posts -->
|
|
<div class="sidebar-widget">
|
|
<h3 class="widget-title">Related Scrolls</h3>
|
|
<ul class="related-posts-list">
|
|
<li class="related-post-item">
|
|
<a href="#" class="related-post-link">
|
|
<div class="related-post-thumb" style="background-image: url('/api/placeholder/100/100')"></div>
|
|
<div class="related-post-info">
|
|
<h4 class="related-post-title">ESP32 Ethereal Connections: Building a Wireless Sensor Network</h4>
|
|
<span class="related-post-date">March 30, 2025</span>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="related-post-item">
|
|
<a href="#" class="related-post-link">
|
|
<div class="related-post-thumb" style="background-image: url('/api/placeholder/100/100')"></div>
|
|
<div class="related-post-info">
|
|
<h4 class="related-post-title">The Dark Arts of Memory Management in C++</h4>
|
|
<span class="related-post-date">April 22, 2025</span>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="related-post-item">
|
|
<a href="#" class="related-post-link">
|
|
<div class="related-post-thumb" style="background-image: url('/api/placeholder/100/100')"></div>
|
|
<div class="related-post-info">
|
|
<h4 class="related-post-title">Securing the Digital Realm: STM32 Encryption Techniques</h4>
|
|
<span class="related-post-date">February 15, 2025</span>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
<!-- Footer Section -->
|
|
<footer class="footer">
|
|
<div class="footer-content">
|
|
<div class="social-links">
|
|
<a href="#" class="social-icon"><i class="fab fa-github"></i></a>
|
|
<a href="#" class="social-icon"><i class="fab fa-linkedin"></i></a>
|
|
<a href="#" class="social-icon"><i class="fab fa-twitter"></i></a>
|
|
<a href="#" class="social-icon"><i class="fab fa-discord"></i></a>
|
|
<a href="#" class="social-icon"><i class="fas fa-envelope"></i></a>
|
|
<a href="#" class="social-icon"><i class="fab fa-hackerrank"></i></a>
|
|
</div>
|
|
<p class="copyright">© 2025 SecCodeSmith. All rights forged in digital fire.</p>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
</html> |