مرحبا يا هابر!
لعدة سنوات حتى الآن ، كنت أقوم بجميع مشاريعي لخط متحكم stm32 في C ++.
خلال هذا الوقت ، جمعت كمية معينة من المواد التي قد تهم مطورين آخرين.
لتجنب الأسئلة غير الضرورية: أستخدم مجموعة من QtCreator + gcc + gdb + openocd. لقد تم وصف كيفية العمل معها عدة مرات ، لذلك لن أتوقف عند هذا الحد ، ولكن سأخبرك بالمزيد عن أساليبي للعمل مع وحدات التحكم الدقيقة.
في المستوى الأدنى من المشروع ، كقاعدة عامة ، هناك محركات محيطية. لنبدأ معهم.
أحاول ، قدر الإمكان ، عدم العبث مع الوحوش مثل SPL و HAL و ، سامحني الله ، CubeMX ، باستثناء إنهاء مشاريع الآخرين من أجل المال. إن أيديولوجية الإطار "كما هو الحال في الآلات الكبيرة" ، في رأيي ، شريرة هناك. تتصور الوظائف المرنة والمريحة للاستخدام (على الرغم من أن البعض سيجادل في ذلك) تتحول إلى رمز آلة كبير إلى حد ما دون المستوى الأمثل ، حيث يتم تنفيذ العديد من العمليات غير الضرورية على وحدة التحكم الدقيقة في وقت التشغيل. نعم ، وحدات التحكم الحديثة قوية ، لكن جهاز الكمبيوتر الذي نجمع عليه المشروع أقوى بكثير ، لذا دعها تفعل كل شيء.
لقد حددت لنفسي المتطلبات الأساسية لتنفيذ الإدارة الطرفية:
- يجب حساب جميع التعبيرات الثابتة في مرحلة التجميع وتندرج في رمز الآلة كأرقام ؛
- يجب على المترجم أن يقوم بنصيب الأسد من العمل ، بالإضافة إلى التحكم في الأنواع التي تم تمريرها إلى الوظائف ؛
- وحدات الماكرو
لا حاجة يجب استخدام الحد الأدنى ، حيث لا يؤدي ذلك إلى إضعاف التحكم في النوع ؛ - يجب أن تدعم واجهة برمجة التطبيق إمكانية قراءة التعليمات البرمجية.
ربما كسول فقط لم يخترع دراجته للعمل مع حقول التسجيل. هذا هو التحول بالإضافة إلى العمليات المنطقية و CMSIS ، وجميع أنواع وحدات الماكرو التي تخفي نفس العمليات في أحشاءها المعقدة وهي في نمط المكتبات من ST. في وقت من الأوقات ، عملت حتى من خلال حقول صغيرة ، حتى توصلت أخيرًا إلى حل ، أود أن أقدمه لك.
في ما يلي ، كمثال ، سأستخدم المحبوب جدًا بواسطة STM32f103 الصيني وسجل التحكم في الوصول إلى الفلاش ، وهو موجود هناك ، لذلك سيكون المثال بسيطًا وقصيرًا.

flash CMSIS 72:
FLASH->ACR = (FLASH->ACR &
(~(FLASH_ACR_LATENCY_Msk
| FLASH_ACR_PRFTBE_Msk )))
| FLASH_ACR_LATENCY_1
| FLASH_ACR_PRFTBE;
, ? , - , , . . , , :
0x80001ec 04 4a ldr r2, [pc, #16] ; (0x8000200 <main()+20>)
0x80001ee <+ 2> 13 68 ldr r3, [r2, #0]
0x80001f0 <+ 4> 23 f0 17 03 bic.w r3, r3, #23
0x80001f4 <+ 8> 43 f0 12 03 orr.w r3, r3, #18
0x80001f8 <+ 12> 13 60 str r3, [r2, #0]
, : --. ?
. .
, . . flash :
struct Regs {
uint32_t ACR;
};
:
struct ACR {
constexpr static uint8_t LATENCY[]{ 0, 3 };
constexpr static uint8_t HLFCYA[]{ 3, 1 };
constexpr static uint8_t PRFTBE[]{ 4, 1 };
constexpr static uint8_t PRFTBS[]{ 5, 1 };
};
— , — . Python SVD .
, . constexpr :
constexpr static uint32_t base = 0x40022000;
INLINE constexpr static volatile Regs* rg()
{
return reinterpret_cast<volatile Regs*>(base);
}
base , (, , , , ) . .
INLINE
#ifndef INLINE
#define INLINE __attribute__((__always_inline__)) inline
#endif
, . , , gcc .
flash :
INLINE static void setLatency(Flash::Latency latency, bool prefetchBufferEnable = false)
{
setRegister(rg()->ACR,
ACR::LATENCY, static_cast<uint8_t>(latency),
ACR::PRFTBE, prefetchBufferEnable
);
}
, : , . latency , . static_cast<uint8_t>(latency) , , , Latency :
enum class Latency : uint8_t {
zeroWaitState = 0b000,
oneWaitState = 0b001,
twoWaitStates = 0b010
};
Flash::setLatency(Flash::Latency::twoWaitStates, true);
:
0x80001ec 04 4a ldr r2, [pc, #16] ; (0x8000200 <main()+20>)
0x80001ee <+ 2> 13 68 ldr r3, [r2, #0]
0x80001f0 <+ 4> 23 f0 17 03 bic.w r3, r3, #23
0x80001f4 <+ 8> 43 f0 12 03 orr.w r3, r3, #18
0x80001f8 <+ 12> 13 60 str r3, [r2, #0]
CMSIS: --, .
? setRegister. :
template<typename T, typename V, typename... Args>
INLINE constexpr static void setRegister(volatile uint32_t& reg,
const T field,
const V value,
const Args... args)
{
uint32_t mask = setMaskR(field, value, args...);
uint32_t val = setBitsR(field, value, args...);
reg = (reg & (~mask)) | val;
}
( uint32_t) — . .
:
template<typename V, typename T>
INLINE constexpr static uint32_t setBitsR(T field, V val)
{
return (val << (field[0]));
}
template<typename V, typename T, typename... Args>
INLINE constexpr static uint32_t setBitsR(T field, V val, Args... args)
{
return (val << (field[0])) | setBitsR(args...);
}
template<typename V, typename T>
INLINE constexpr static uint32_t setMaskR(T field, V val)
{
return ((((1 << field[1]) - 1) << field[0]));
}
template<typename V, typename T, typename... Args>
INLINE constexpr static uint32_t setMaskR(T field, V val, Args... args)
{
return ((((1 << field[1]) - 1) << field[0])) | setMaskR(args...);
}
"" . " " , , , , , , .
setRegister? . , - - — . , , , , volatile, , . , , .
. :
Flash::setLatency(Flash::Latency::twoWaitStates, true);
Flash::setLatency(Flash::Latency::oneWaitState, true);
--:
0x80001ec 07 4a ldr r2, [pc, #28] ; (0x800020c <main()+32>)
0x80001ee <+ 2> 13 68 ldr r3, [r2, #0]
0x80001f0 <+ 4> 23 f0 17 03 bic.w r3, r3, #23
0x80001f4 <+ 8> 43 f0 12 03 orr.w r3, r3, #18
0x80001f8 <+ 12> 13 60 str r3, [r2, #0] ;
0x80001fa <+ 14> 13 68 ldr r3, [r2, #0]
0x80001fc <+ 16> 23 f0 17 03 bic.w r3, r3, #23
0x8000200 <+ 20> 43 f0 11 03 orr.w r3, r3, #17
0x8000204 <+ 24> 13 60 str r3, [r2, #0] ;
:
template<typename T>
INLINE constexpr static uint32_t getRegField(volatile uint32_t& reg,
const T field)
{
uint32_t mask = (((1 << field[1]) - 1) << field[0]);
return ((reg & mask) >> field[0]);
}
.
— :
INLINE static bool getLatencyPrefetch()
{
return getRegField(rg()->ACR,
ACR::LATENCY,
ACR::PRFTBE);
}
flash :
struct Flash {
constexpr static uint32_t base = 0x40022000;
struct ACR {
constexpr static uint8_t LATENCY[]{ 0, 3 };
constexpr static uint8_t HLFCYA[]{ 3, 1 };
constexpr static uint8_t PRFTBE[]{ 4, 1 };
constexpr static uint8_t PRFTBS[]{ 5, 1 };
};
enum class Latency : uint8_t {
zeroWaitState = 0b000,
oneWaitState = 0b001,
twoWaitStates = 0b010
};
INLINE constexpr static volatile Regs* rg()
{
return reinterpret_cast<volatile Regs*>(base);
}
INLINE static void setLatency(Flash::Latency latency, bool prefetchBufferEnable = false)
{
Utility::setRegister(rg()->ACR,
ACR::LATENCY, static_cast<uint8_t>(latency),
ACR::PRFTBE, prefetchBufferEnable
);
}
INLINE static bool getLatencyPrefetch()
{
return getRegField(rg()->ACR,
ACR::LATENCY,
ACR::PRFTBE);
}
};
. , , . .
- , .
, . , DSP-. — .