STM32PWMレジスタは任意の出力を構成します

クロックを有効にする

出力PWMは2つのクロックを有効にする必要があります。1つはピンのクロックで、もう1つはタイマーのクロックです。

タイマー時計

RCCのRCC_APB1ENRレジスタを見てください。
ここに画像の説明を挿入
たとえば、ライブラリ関数を呼び出すことによって:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

ピンクロックの初期化

RCCのRCC_AHB1ENRレジスタを確認してください。
ここに画像の説明を挿入
最初に以下を列挙して、すべてのピンをリ​​ストします。

typedef enum // 枚举管脚编号 
{
    
    
    //  A端口     //0~31
    A0,  A1,  A2,  A3,  A4,  A5,  A6,  A7,  A8,  A9,  A10, A11, A12, A13, A14, A15,
    A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31,

    //  B端口     //32~63
    B0,  B1,  B2,  B3,  B4,  B5,  B6,  B7,  B8,  B9,  B10, B11, B12, B13, B14, B15,
    B16, B17, B18, B19, B20, B21, B22, B23, B24, B25, B26, B27, B28, B29, B30, B31,

    //  C端口     //64~95
    C0,  C1,  C2,  C3,  C4,  C5,  C6,  C7,  C8,  C9,  C10, C11, C12, C13, C14, C15,
    C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26, C27, C28, C29, C30, C31,

    //  D端口     //96~127
    D0,  D1,  D2,  D3,  D4,  D5,  D6,  D7,  D8,  D9,  D10, D11, D12, D13, D14, D15,
    D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D30, D31,

    //  E端口     //128~159
    E0,  E1,  E2,  E3,  E4,  E5,  E6,  E7,  E8,  E9,  E10, E11, E12, E13, E14, E15,
    E16, E17, E18, E19, E20, E21, E22, E23, E24, E25, E26, E27, E28, E29, E30, E31
		
}PTX_n;

次に、上の図のレジスタを構成して、ピンクロックを構成します。

#define PTX(PTX_n)  (PTX_n>>5)  
#define PTn(PTX_n)  (PTX_n&0x1f)

	vuint8 ptx,ptn;
    
    ptx = PTX(ptx_n);
    ptn = PTn(ptx_n);

	RCC->AHB1ENR|= (uint32_t )(RCC_AHB1Periph_GPIOA << ptx);

ピン多重化構成

多重化することにより、ピンをpwm出力のチャネルとして構成します。

//默认上拉
void port_init(PTX_n ptx_n, PROT_AF af)
{
    
    
		vuint8 ptx,ptn;
    
    	ptx = PTX(ptx_n);
    	ptn = PTn(ptx_n);

		RCC->AHB1ENR|= (uint32_t )(RCC_AHB1Periph_GPIOA << ptx);
		//配置复用模式
		GPIO[ptx]->MODER |= (0x02 << ((uint16_t)ptn * 2));
		//配置上下拉电阻
		GPIO[ptx]->PUPDR &=  ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)ptn * 2));
		GPIO[ptx]->PUPDR |= (GPIO_PUPDR_PUPDR0_1 << ((uint16_t)ptn * 2));
	
		vuint8 mov = ptn & 0X07;
		GPIO[ptx]->AFR[ptn>>3] &= ~(uint32)(0x0f << (mov << 2));
		GPIO[ptx]->AFR[ptn>>3] |= (uint32)(af << (mov << 2));
}

上記のGPIOアレイは何を保存しますか?

#define GPIO_BASE_PTRS		{GPIOA, GPIOB, GPIOC, GPIOD, GPIOE}
static GPIO_TypeDef * const GPIO[] = GPIO_BASE_PTRS;

このようにして、レジスタの構成がより便利になります。

対応するPORT_AFパラメータを取得するにはどうすればよいですか?
データシートを参照して入手可能
ここに画像の説明を挿入

PWM出力構成

上記のGPIOと同様に、レジスタの構成を容易にするために、ベースアドレスを取得して強制変換する方法が採用されています。

#define TIM_BASE_PTRS		{TIM1, TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM8, TIM9}
static TIM_TypeDef * const TIM[] = TIM_BASE_PTRS;

自動ロード値と分周器を設定します

対応するTIMペリフェラルのTIMx_ARRレジスタとTIMx_PSCレジスタを確認してください。

ここに画像の説明を挿入
ここに画像の説明を挿入

TIM[tim_n]->ARR = 9999;//占空比0~10000
TIM[tim_n]->PSC = frequency;

ここでのクロックソースは、上記のAPB1(最初に有効にされたクロック)です。

PWM出力モードを構成します

TIMのCCMRレジスタは主にここで構成されます。そのうち2〜5TIMには4つの出力チャネルがあり、9〜14TIMには1つの出力チャネルしかなく、CCMRレジスタの数も異なります。
ここに画像の説明を挿入

		TIM[tim_n]->DIER|=1<<0;
		if (ch < TIM_ch3)
		{
    
    
				TIM[tim_n]->CCMR1 |= 6 << (4 + (ch << 3));
				TIM[tim_n]->CCMR1 |= 1 << (3 + (ch << 3));
		}
		else
		{
    
    
				TIM[tim_n]->CCMR2 |= 6 << (4 + ((ch - 2) << 3));
				TIM[tim_n]->CCMR2 |= 1 << (3 + ((ch - 2) << 3));
		}

チャネルイネーブル

主に、TIMペリフェラルでTIMx_CCMR1レジスタまたはTIMx_CCMR2レジスタを設定します。
ここに画像の説明を挿入
ここに画像の説明を挿入
構成:

		TIM[tim_n]->CCER |= 1<<(ch << 2);//输出使能
		TIM[tim_n]->CCER |= 1<<(1 + (ch << 2));

デューティサイクル構成

TIMでTIMx_CCRnレジスタを設定する。ここで、nビットはチャネルに対応します。
ここに画像の説明を挿入
これは32ビットレジスタであり、アドレスが連続しているため、プログラミングを容易にするために、CCR1に基づく次の加算方法を使用して構成します。

*(( __IO uint32 *)&(TIM[tim_n]->CCR1) + ch * 4) = duty;

有効

TIMでTIMx_CR1レジスタを設定する。
ここに画像の説明を挿入
ここでは、ARPEビットとCENビットが主に構成されており、それぞれ自動ロードイネーブルとカウンターイネーブルを表しています。



終わり

構成の過程で、ビット演算の優先順位に注意してください。安全のために括弧を使用するのが最善です。
完全なコードを添付してください:

void pwm_pin_mux(TIMx_n tim_n, TIM_ch ch)
{
    
    
		if (tim_n == TIM_2)
		{
    
    
				RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
				if (ch == TIM_ch1)
						port_init(A0, PORT_AF1);
				else if (ch == TIM_ch2)
						port_init(A1, PORT_AF1);
				else if (ch == TIM_ch3)
						port_init(A2, PORT_AF1);
				else if (ch == TIM_ch4)
						port_init(A3, PORT_AF1);
		}
		if (tim_n == TIM_3)
		{
    
    
				RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
				if (ch == TIM_ch1)
						port_init(B4, PORT_AF2);
				else if (ch == TIM_ch2)
						port_init(B5, PORT_AF2);
				else if (ch == TIM_ch3)
						port_init(B0, PORT_AF2);
				else if (ch == TIM_ch4)
						port_init(B1, PORT_AF2);
		}
		if (tim_n == TIM_4)
		{
    
    
				RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
				if (ch == TIM_ch1)
						port_init(B6, PORT_AF2);
				else if (ch == TIM_ch2)
						port_init(B7, PORT_AF2);
				else if (ch == TIM_ch3)
						port_init(B8, PORT_AF2);
				else if (ch == TIM_ch4)
						port_init(B9, PORT_AF2);
		}
		if (tim_n == TIM_5)
		{
    
    
				RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
				if (ch == TIM_ch1)
						port_init(A0, PORT_AF2);
				else if (ch == TIM_ch2)
						port_init(A1, PORT_AF2);
				else if (ch == TIM_ch3)
						port_init(A2, PORT_AF2);
				else if (ch == TIM_ch4)
						port_init(A3, PORT_AF2);
		}
}


void pwm_init(TIMx_n tim_n, TIM_ch ch, uint32 duty)
{
    
    
		//使能时钟	引脚配置
		pwm_pin_mux(tim_n, ch);
	
		TIM[tim_n]->ARR = 9999;//占空比0~10000
		TIM[tim_n]->PSC = 1;//默认不分频
		TIM[tim_n]->DIER|=1<<0;
		if (ch < TIM_ch3)
		{
    
    
				TIM[tim_n]->CCMR1 |= 6 << (4 + (ch << 3));
				TIM[tim_n]->CCMR1 |= 1 << (3 + (ch << 3));
		}
		else
		{
    
    
				TIM[tim_n]->CCMR2 |= 6 << (4 + ((ch - 2) << 3));
				TIM[tim_n]->CCMR2 |= 1 << (3 + ((ch - 2) << 3));
		}
		TIM[tim_n]->CCER |= 1<<(ch << 2);//输出使能
		TIM[tim_n]->CCER |= 1<<(1 + (ch << 2));   
		
		*(( __IO uint32 *)&(TIM[tim_n]->CCR1) + ch * 4) = duty;
		TIM[tim_n]->CR1  |= 1<<7;   		//ARPE使能 
		TIM[tim_n]->CR1  |= 1<<0;    		//使能定时器
		
		
}

使用するときに呼び出すことができます。次に例を示します。

pwm_init(TIM_3, TIM_ch2, 1000);

おすすめ

転載: blog.csdn.net/qq_43550173/article/details/109691173
おすすめ