1 สร้าง Floder ชื่อ GPIO ขึ้นมา
2 ทำการเพื่ม File system_stm32f4xx.c และ stm32f4xx.hไว้ใน Floder
3 ทำการสร้างโปรเจคใหม่ เลือก เบอร์ IC stm32f407VG และให้ ADD File startup_stm32f4.s
เข้ามาด้วย เพื่อที่จะได้ไม่ต้องไปก๊อปมาจาก Lib st ให้ยุ่งยากอีก ความสำคัญคือมันจะช่วยจัดการค่า
เบื้องต้นให้กับ IC ให้พร้อมรับการโปรแกรมและการทำงาน ถ้าไม่มี MCU จะ compiler ไม่ผ่าน
4 เขียนClode และ save โปรเจคเป็นนามสกุล .C
เว๊ปไซด์อ้างอิงและให้อ่านศึกษาต่อยอด
http://blog.mark-stevens.co.uk/?p=854
http://jeremyherbert.net/get/stm32f4_getting_started
http://www.farrellf.com/projects/hardware/2012-06-28_STM32F4_Basics:_GPIOs/
ขั้นตอนแรกการควบคุม GPIO ครับ
ขา IC แต่ละขา มีหน้าที่ทำงานต่างๆ และต่างกัน ในขาเดียวกันอาจมีสามหรือสี่ฟังก์ชันในขาเดียวกัน หรือ ไม่มีหน้าที่เลย (NC)
แล้ว NC มีไว้ทำอะไร ประโยชน์ที่เห็นมี 2 อย่าง (1) ไว้ช่วยยึดตัว IC (2) ความสวยงามของตัวถัง ถ้าไม่มีไม่เท่
กลับมาต่อครับ มารู้จัก รีจีสเตอร์ที่ควบคุม GPIOกัน
Register ที่ควรศึกษา
GPIOx->MODER
GPIOx->OTYPER 0-PushPull, 1-OpenDrain
GPIOx->OSPEEDR Output SPEED Register : 00-2, 01-25, 10-50, 11-100 [MHz]
GPIOx->PUPDR PullUp PullDown Register : 00-none, 01-Pup, 10-Pdn, 11-reserved
GPIOx->IDR Input Data Register
GPIOx->ODR Output Data Register
GPIOx->BSRRL Bit Set Reset Register Hi: bit sets PortPin
GPIOx->BSRRH Bit Set Reset Register Lo: bit resets PortPin
GPIOx->LCKR LoCK Register
GPIOx->AFR[0] Alternative Function Register Lo - select 1of16
GPIOx->AFR[1] Alternative Function Register Hi - select 1of16
ถ้าท่านใช้ขาที่เลือกเป็น Digital Output จะต้องดูที่รีจีสเตอร์ MODER
GPIO จะมีขาให้ใช้มากสุด 15 ขา หรือ 15 bit เช่น GPIOA 0;........GPIOA14 , GPIOA15 .
ขา GPIO แต่ละบิด จะใช้ 2 บิตของรีจีสเตอร์ MODER ควบคุม ดังนี้
GPIO(Bit0) = MODER Bit0(x) กับ MODER Bit1(y) ควบคุม
GPIO(Bit1) = MODER Bit2(x) กับ MODER Bit3(y) ควบคุม
GPIO(Bit2) = MODER Bit4(x) กับ MODER Bit5(y) ควบคุม
. .
. .
GPIO(Bit14) = MODER Bit28(x) กับ MODER Bit29(y) ควบคุม
GPIO(Bit15) = MODER Bit30(x) กับ MODER Bit31(y) ควบคุม
(x),(y) คือค่าทางลอจิของบิตนั้นๆ
Output ต้อง Set x = 1, Set y = 0 อ้างอิงจาก Reference Manual RM0090 หน้า186/1422 ST
เช่นต้องการให้ Bit 0, Bit 1 ของ GPIOA เป็น เอาท์พุต ได้ GPIOA->MODER = 0x00000005;
ต้องการให้ Bit 0, Bit 15 ของ GPIOB เป็น เอาท์พุต ได้ GPIOB->MODER = 0x10000001;
ถัดมา รีจีสเตอร์ปรับความเร็วที่GPIO
เราสามารถปรับความเร็วของขาแต่ละขาได้จากรีจีสเตอร์ OSPEEDR ครับ 32bit คล้าย MODER
GPIO(Bit0) = OSPEEDR Bit0(x) กับ OSPEEDR Bit1(y) ควบคุม
GPIO(Bit1) = OSPEEDR Bit2(x) กับ OSPEEDR Bit3(y) ควบคุม
GPIO(Bit2) = OSPEEDR Bit4(x) กับ OSPEEDR Bit5(y) ควบคุม
. .
. .
GPIO(Bit14) = OSPEEDR Bit28(x) กับ OSPEEDR Bit29(y) ควบคุม
GPIO(Bit15) = OSPEEDR Bit30(x) กับ OSPEEDR Bit31(y) ควบคุม
(x),(y) คือค่าทางลอจิของบิตนั้นๆ
(x = 0, y = 0) ขานั้นจะมีความเร็วสูงสุดแค่ 2 Mhz อ้างอิงจาก Reference Manual RM0090 หน้า199/1422 ST
(x = 0, y = 1) ขานั้นจะมีความเร็วสูงสุดแค่ 25 Mhz
(x = 1, y = 0) ขานั้นจะมีความเร็วสูงสุดแค่ 50 Mhz
(x = 1, y = 1) ขานั้นจะมีความเร็วสูงสุดแค่ 100 Mhz
ตัวอย่าง
ต้องการขา GPIOB Bit0 เป็น 25Mhz Bit15 เป็น 100Mhz ได้ GPIOB->SPEEDR = 0xC0000001;
ถัดมารีจีสเตอร์ PUPDR ใช้ในการทำ พูอัป พูดาว ของตัวต้านทานที่ขานั้นๆ หลักการคล้ายกับ MODER,OSPEEDR
GPIO(Bit0) = PUPDR Bit0(x) กับ PUPDR Bit1(y) ควบคุม
GPIO(Bit1) = PUPDR Bit2(x) กับ PUPDR Bit3(y) ควบคุม
GPIO(Bit2) = PUPDR Bit4(x) กับ PUPDR Bit5(y) ควบคุม
. .
. .
GPIO(Bit14) = PUPDR Bit28(x) กับ PUPDR Bit29(y) ควบคุม
GPIO(Bit15) = PUPDR Bit30(x) กับ PUPDR Bit31(y) ควบคุม
(x = 0, y = 0) No Pull-up, No Pull-down อ้างอิงจาก Reference Manual RM0090 หน้า200/1422 ST
(x = 0, y = 1) Pull-up
(x = 1, y = 0) Pull-down
(x = 1, y = 1) (ไม่ใช้งาน ไม่เกิดอะไรขึ้น)
อีกตัวที่สำคัญคือ รีจีสเตอร์ OTYPER
OTYPER จะมีแค่ 15 bit ตามจำนวนขาของ GPIO ดังนั้นคำสั่งจะมีแค่ ลอจิก 0 กับ 1
GPIO(Bit0) = OTYPER Bit0(x)
GPIO(Bit1) = OTYPER Bit1(x)
. .
GPIO(Bit15) = OTYPER Bit15(x)
ถ้า x = 0 bitนั้น = Output Push-Pull
ถ้า x = 1 bitนั้น = Output Open drain
..............................................................................
GPIOx ชื่อของ Port ต่างๆ เช่น GPIOA,GPIOB......
GPIOx-> เป็นรูปแบบการใช้งาน
GPIOA->ODR เมื่อ ODR คือการกระทำตัวรีจีสเตอร์ทั้ง 32bit หมายเหตุ รีจีสเตอร์มี 32bit แต่ว่าขาที่ต่อ
ไม่จำเป็นต้องมี 32 ขา ในค่ายของ ST cortex M จะมีขาให้ใช้มากสุด 15 ขา หรือ 15bit
ตัวอย่าง GPIOA->ODR = 0x1000F281; แต่รีจีสเตอร์ GPIOA แสดงออกที่ขาได้แค่ 0xF281
ใน #include "stm32f4xx.h" จะมีการบบรจุตัวอ้างอิงใช้สือความหมายแทนค่าระดับ bit และ แทนรีจีสเตอร์ได้
เราสามารถนำค่าเหล่านั้นมาใช้เพื่อสื่อความหมายในการเขียนโปรแกรมได้
/****************** Bits definition for GPIO_MODER register *****************/
#define GPIO_MODER_MODER0 ((uint32_t)0x00000003) // เซ็ทเป็น อะนาล็ก บิท 0
#define GPIO_MODER_MODER0_0 ((uint32_t)0x00000001) // เซ็ทเป็น เอาท์พุท บิท 0
#define GPIO_MODER_MODER0_1 ((uint32_t)0x00000002) // เซ็ทเป็นขาฟังก์ชันร่วม บิท 0
#define GPIO_MODER_MODER1 ((uint32_t)0x0000000C) // เซ็ทเป็น อะนาล็ก บิท 1
#define GPIO_MODER_MODER1_0 ((uint32_t)0x00000004) // เซ็ทเป็น เอาท์พุท บิท 1
#define GPIO_MODER_MODER1_1 ((uint32_t)0x00000008) // เซ็ทเป็นขาฟังก์ชันร่วม บิท 1
#define GPIO_MODER_MODER2 ((uint32_t)0x00000030)
#define GPIO_MODER_MODER2_0 ((uint32_t)0x00000010)
#define GPIO_MODER_MODER2_1 ((uint32_t)0x00000020)
#define GPIO_MODER_MODER3 ((uint32_t)0x000000C0)
#define GPIO_MODER_MODER3_0 ((uint32_t)0x00000040)
#define GPIO_MODER_MODER3_1 ((uint32_t)0x00000080)
#define GPIO_MODER_MODER4 ((uint32_t)0x00000300)
#define GPIO_MODER_MODER4_0 ((uint32_t)0x00000100)
#define GPIO_MODER_MODER4_1 ((uint32_t)0x00000200)
#define GPIO_MODER_MODER5 ((uint32_t)0x00000C00)
#define GPIO_MODER_MODER5_0 ((uint32_t)0x00000400)
#define GPIO_MODER_MODER5_1 ((uint32_t)0x00000800)
#define GPIO_MODER_MODER6 ((uint32_t)0x00003000)
#define GPIO_MODER_MODER6_0 ((uint32_t)0x00001000)
#define GPIO_MODER_MODER6_1 ((uint32_t)0x00002000)
#define GPIO_MODER_MODER7 ((uint32_t)0x0000C000)
#define GPIO_MODER_MODER7_0 ((uint32_t)0x00004000)
#define GPIO_MODER_MODER7_1 ((uint32_t)0x00008000)
#define GPIO_MODER_MODER8 ((uint32_t)0x00030000)
#define GPIO_MODER_MODER8_0 ((uint32_t)0x00010000)
#define GPIO_MODER_MODER8_1 ((uint32_t)0x00020000)
#define GPIO_MODER_MODER9 ((uint32_t)0x000C0000)
#define GPIO_MODER_MODER9_0 ((uint32_t)0x00040000)
#define GPIO_MODER_MODER9_1 ((uint32_t)0x00080000)
#define GPIO_MODER_MODER10 ((uint32_t)0x00300000)
#define GPIO_MODER_MODER10_0 ((uint32_t)0x00100000)
#define GPIO_MODER_MODER10_1 ((uint32_t)0x00200000)
#define GPIO_MODER_MODER11 ((uint32_t)0x00C00000)
#define GPIO_MODER_MODER11_0 ((uint32_t)0x00400000)
#define GPIO_MODER_MODER11_1 ((uint32_t)0x00800000)
#define GPIO_MODER_MODER12 ((uint32_t)0x03000000)
#define GPIO_MODER_MODER12_0 ((uint32_t)0x01000000)
#define GPIO_MODER_MODER12_1 ((uint32_t)0x02000000)
#define GPIO_MODER_MODER13 ((uint32_t)0x0C000000)
#define GPIO_MODER_MODER13_0 ((uint32_t)0x04000000)
#define GPIO_MODER_MODER13_1 ((uint32_t)0x08000000)
#define GPIO_MODER_MODER14 ((uint32_t)0x30000000)
#define GPIO_MODER_MODER14_0 ((uint32_t)0x10000000)
#define GPIO_MODER_MODER14_1 ((uint32_t)0x20000000)
#define GPIO_MODER_MODER15 ((uint32_t)0xC0000000) // เซ็ทเป็น อะนาล็ก บิท 15
#define GPIO_MODER_MODER15_0 ((uint32_t)0x40000000) // เซ็ทเป็น เอาท์พุท บิท 1
#define GPIO_MODER_MODER15_1 ((uint32_t)0x80000000) // เซ็ทเป็นขาฟังก์ชันร่วม บิท 15
เช่นต้องการให้ Bit 0, Bit 1 ของ GPIOA เป็น เอาท์พุต ได้ GPIOA->MODER = GPIO_MODER_MODER0_0|GPIO_MODER_MODER1_0 ;
ต้องการให้ Bit 0, Bit 15 ของ GPIOB เป็น เอาท์พุต ได้ GPIOB->MODER = GPIO_MODER_MODER0_0|GPIO_MODER_MODER15_0 ;
สุดท้ายอย่าลืมปล่อยสัญญาณ clock ให้ Port ที่ใช้งานด้วยครับเดี๋ยวจะไม่ทำงานกัน หลับปุ๋ย
#include"stm32f4xx.h"
void delay(long signed count) // ฟังก์ชันหน่วงเวลา
{
while(count--);
}
void clock() // ฟังก์ชัน ฐานเวลา
{
RCC->CFGR = RCC_CFGR_HPRE_DIV1|RCC_CFGR_PPRE1_DIV4|RCC_CFGR_PPRE2_DIV2 ;
RCC->CR = 0;
RCC->CR |= RCC_CR_HSEON; // ให้ HSE Xtal เป็น ฐานเวลาหลัก
while (!(RCC->CR & 0x00020000));// รอเวลาให้ Xtal osc ทำงาน
RCC->PLLCFGR = (RCC_PLLCFGR_PLLQ_2|RCC_PLLCFGR_PLLQ_1|RCC_PLLCFGR_PLLQ_0
|RCC_PLLCFGR_PLLSRC_HSE);
RCC->PLLCFGR |= (RCC_PLLCFGR_PLLN_8 |RCC_PLLCFGR_PLLN_6 |RCC_PLLCFGR_PLLN_4);
// เซ็ทค่าความถี่ และตัวคูณต่าง ดูข้อมูลใน Ref-manual stm32f407xx
// หัวข้อการเซ็ทอัป system clock
RCC->CR |= RCC_CR_PLLON; // สั่งให้ตัวคูณ PLL ทำงาน
while (!(RCC->CR & 0x02000000)); // รอจนกว่า PLL พร้อมทำงาน
FLASH->ACR = 0x00000605; // จัดเวลา Flash ROM is 5 Wait state
RCC->CFGR |= 0x00000002; // เลือกสัญญาณหลัก System PLL On
while ((RCC->CFGR & 0x0000000F) != 0x0000000A); // รอระบบ Wait system Pll พร้อมทำงาน
}
int main(void) // ฟังก์ชันโปรแกรมหลัก
{
clock();
GPIOD->OSPEEDR = 0xFFFF; // Set Speed GPIOD is 100Mhz
RCC->AHB1ENR |= 0x08; // Open clock to GPIOD
GPIOD->MODER = (GPIO_OSPEEDER_OSPEEDR15_0 |GPIO_OSPEEDER_OSPEEDR14_0 |
GPIO_OSPEEDER_OSPEEDR13_0 |GPIO_OSPEEDER_OSPEEDR12_0);
// Set GPIO D.15,D.14,D.13,D.12 is Output
while(1) // ลูปวนค่าไม่รู้จบ
{
GPIOD->ODR ^= 0xF000; // กลับค่าจากค่าเดิมก่อนหน้านี้
delay(50000000);
}
}
หรืออีกรูปแบบ
#include"stm32f4xx.h"
//CPU 8 Mhz Xtal OSC and PLL is 168Mhz
//AHB system clock 168 Mhz
//APB1 system clock 42 Mhz
//APB2 system clock 84 Mhz
void delay(long signed count)
{
for(;count>0;count--);
}
void clock_setup(void)
{
RCC->CFGR = 0;
RCC->CR = 0;
RCC->CR |= 0x00010000; // select HSE Xtal is main clock
while (!(RCC->CR & 0x00020000));// wait time to Xtal osc stability
RCC->PLLCFGR = 0x07405408; // Set PLL M=8, N=336, P=2 ve Q=7
RCC->CR |= 0x01000000; // PLL On
while(!(RCC->CR & 0x02000000))
FLASH->ACR = 0x00000605; // Flash ROM is 5 Wait state
RCC->CFGR |= 0x00000002; // System PLL On
while ((RCC->CFGR & 0x0000000F) != 0x0000000A); //
}
int main(void)
{
clock_setup();
GPIOD->OSPEEDR = 0xFFFF; // Set Speed GPIOD is 100Mhz
RCC->AHB1ENR |= 0x08; // Open clock to GPIOD
GPIOD->MODER = 0x55000000; // Set GPIO D.15,D.14,D.13,D.12 is Output
while(1)
{
GPIOD->ODR ^= 0xF000;
delay(50000000);
}
}
}
เสร็จแล้วหนึ่งโปรเจ็คไฟกระพริบสี่ดวงพร้อมกัน ชักจะเป็นแล้วครับ Thank! :D
งง เรื่องการเซ็ตอัพมาตั้งแต่บทแรกเลยครับ มีพื้นฐานมาแค่ arduino พี่ๆมีคำแนะนำไหมครับ T^T
ซัพเฉพาะ เยอะมากเริ่มจากไหนดีครับ
สุดยอดเลยครับ เขียน STM32 ได้โดยไม่ต้องง้อ Library
น่าจะลด code size ลงได้เยอะมาก
ซื้อบอร์ดใหม่มาแล้วครับ ที่เคยศึกษาไว้ลืมไปหมดแล้ว มีโพสต์ก็ดีอย่างนี้แหละครับ ข้อมูลยังอยู่ >:(
ขอบคุณครับ
มีข้อสงสัยเกี่ยวกับ GPIOx->OSPEEDR อยากสอบถามครับ
คือ OSPEED มีความเกี่ยวข้องกับ System Clock config หรือไม่ครับ ถ้าผมใช้ board stm32d401re nucleo ซึ่ง maximum clock คือ 84MHz ถ้าเลือกใช้ OSPEED = 100MHz จะมีปัญหาไหมครับ
Quote from: Irumi on August 30, 2019, 03:15:26 PM
มีข้อสงสัยเกี่ยวกับ GPIOx->OSPEEDR อยากสอบถามครับ
คือ OSPEED มีความเกี่ยวข้องกับ System Clock config หรือไม่ครับ ถ้าผมใช้ board stm32d401re nucleo ซึ่ง maximum clock คือ 84MHz ถ้าเลือกใช้ OSPEED = 100MHz จะมีปัญหาไหมครับ
ไม่ได้เกี่ยวข้องกันครับ OSPEEDR จริงๆ แล้วใช้ในการปรับ Slew rate หรือก็คือความเร็วในการเปลี่ยนจาก High ไป Low (t
f) หรือ Low ไป High (t
r) ของ pin
การเปลี่ยนค่า OSPEEDR มันจะไปปรับเปลี่ยนวงจร Drive ของ Pin โดยการเพิ่ม Speed ก็เป็นการเพิ่มกระแสให้กับวงจร Driver เพื่อให้สัญญาณมันเปลี่ยนแปลงได้เร็วขึ้น
แต่ก็แลกมากับ noise ที่มากขึ้นไปด้วย แต่ถ้าเราปรับ Speed ต่ำ แต่เอา Pin ไปขับสัญญาณถี่มากๆ สัญญาณมันจะเปลี่ยนแปลงไม่ทันนั่นเอง
เลข MHz ที่ระบุมาเป็นความถี่ที่ ST ได้จากการทดสอบ Pin โดยเอา Pin มาต่อ Load ค่าหนึ่งแล้ว Toggle High Low ในความถี่ค่าหนึ่ง แล้วก็วัดเวลา t
f + t
r เทียบกับคาบของสัญญาณ (T)
ซึ่งความถี่ที่เหมาะสมคือ t
f + t
r ต้องไม่เกิน (2/3)T และชิปแต่ละ Series ก็จะแตกต่างกันไป ซึ่งดูได้จาก Datasheet แต่เวลาใช้งานจริงก็อาจไม่ได้ผลตามที่ระบุ
เพราะมันมีปัจจัยอื่นเข้ามาเสริมด้วยเช่น ขนาดของ Load ที่ pin ขับอยู่ กับ ระดับแรงดันที่ใช้ เป็นต้น แต่ก็ใช้พอจะใช้เป็นค่าอ้างอิงได้ครับ
Quote from: dec on August 31, 2019, 05:29:02 AM
Quote from: Irumi on August 30, 2019, 03:15:26 PM
มีข้อสงสัยเกี่ยวกับ GPIOx->OSPEEDR อยากสอบถามครับ
คือ OSPEED มีความเกี่ยวข้องกับ System Clock config หรือไม่ครับ ถ้าผมใช้ board stm32d401re nucleo ซึ่ง maximum clock คือ 84MHz ถ้าเลือกใช้ OSPEED = 100MHz จะมีปัญหาไหมครับ
ไม่ได้เกี่ยวข้องกันครับ OSPEEDR จริงๆ แล้วใช้ในการปรับ Slew rate หรือก็คือความเร็วในการเปลี่ยนจาก High ไป Low (tf) หรือ Low ไป High (tr) ของ pin
การเปลี่ยนค่า OSPEEDR มันจะไปปรับเปลี่ยนวงจร Drive ของ Pin โดยการเพิ่ม Speed ก็เป็นการเพิ่มกระแสให้กับวงจร Driver เพื่อให้สัญญาณมันเปลี่ยนแปลงได้เร็วขึ้น
แต่ก็แลกมากับ noise ที่มากขึ้นไปด้วย แต่ถ้าเราปรับ Speed ต่ำ แต่เอา Pin ไปขับสัญญาณถี่มากๆ สัญญาณมันจะเปลี่ยนแปลงไม่ทันนั่นเอง
เลข MHz ที่ระบุมาเป็นความถี่ที่ ST ได้จากการทดสอบ Pin โดยเอา Pin มาต่อ Load ค่าหนึ่งแล้ว Toggle High Low ในความถี่ค่าหนึ่ง แล้วก็วัดเวลา tf + tr เทียบกับคาบของสัญญาณ (T)
ซึ่งความถี่ที่เหมาะสมคือ tf + tr ต้องไม่เกิน (2/3)T และชิปแต่ละ Series ก็จะแตกต่างกันไป ซึ่งดูได้จาก Datasheet แต่เวลาใช้งานจริงก็อาจไม่ได้ผลตามที่ระบุ
เพราะมันมีปัจจัยอื่นเข้ามาเสริมด้วยเช่น ขนาดของ Load ที่ pin ขับอยู่ กับ ระดับแรงดันที่ใช้ เป็นต้น แต่ก็ใช้พอจะใช้เป็นค่าอ้างอิงได้ครับ
ขอบคุณ คุณ dec อีกครั้งครับ ที่ช่วยอธิบายให้เข้าใจ ดังนั้นการ Set ค่า OSPEEDR น่าจะ concern 2 หลักๆ คือ 1. power consumtion ของตัว IC และ ก็ EMC