sobota, 5 listopada 2016

[15] STM32 M3 - Nucleo - F103RB - Rejestry przesuwne PWM

Ten post chciałbym poświęcić na opisanie wykonania PWM-u na rejestrach przesuwnych MCP23S08 oraz 74hc595.

MCP23S08


PWM na rejestrze przesuwnym można wykonać tak samo jak na każdym dowolnym pinie mikrokontrolera, tzn poprzez cyklicznie załączenie i wyłączanie danego wyprowadzenia. Co do podłączenia to zostało ono zapisane w poście dotyczącym ekspandera portów. 

Poniżej inicjalizacja SPI oraz GPIO wraz z funkcjami przesyłającymi dany:

  1. #include "stm32f10x.h"
  2. //Deklaracje rejestrów
  3. #define MCP_IODIR  0x00
  4. #define MCP_IPOL  0x01
  5. #define MCP_GPINTEN  0x02
  6. #define MCP_DEFVAL  0x03
  7. #define MCP_INTCON  0x04
  8. #define MCP_IOCON  0x05
  9. #define MCP_GPPU  0x06
  10. #define MCP_INTF  0x07
  11. #define MCP_INTCAP  0x08
  12. #define MCP_GPIO  0x09
  13. #define MCP_OLAT  0x0a
  14. //Deklaracja pinów
  15. #define CS_PIN   GPIO_Pin_0
  16. #define CS_LINE   GPIOC
  17. #define SCK_PIN   GPIO_Pin_5
  18. #define SCK_LINE  GPIOA
  19. #define MISO_PIN  GPIO_Pin_6
  20. #define MISO_LINE  GPIOA
  21. #define MOSI_PIN  GPIO_Pin_7
  22. #define MOSI_LINE  GPIOA
  23. void GPIOInit(void)
  24. {
  25.  GPIO_InitTypeDef GPIOInit;
  26.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
  27.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  28.  GPIO_StructInit(&GPIOInit);
  29.  //piny SCK, MOSI
  30.  GPIOInit.GPIO_Pin = SCK_PIN|MOSI_PIN; // SCK, MOSI
  31.  GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
  32.  GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
  33.  GPIO_Init(SCK_LINE, &GPIOInit);
  34.  //Pin Miso
  35.  GPIOInit.GPIO_Pin = MISO_PIN; // MISO
  36.  GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  37.  GPIOInit.GPIO_Speed = GPIO_Speed_10MHz;
  38.  GPIO_Init(MISO_LINE, &GPIOInit);
  39.  //Pin CS
  40.  GPIOInit.GPIO_Pin = CS_PIN;
  41.  GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP;
  42.  GPIOInit.GPIO_Speed = GPIO_Speed_10MHz;
  43.  GPIO_Init(CS_LINE, &GPIOInit);
  44.  GPIO_SetBits(CS_LINE, CS_PIN);
  45. }
  46. void SPIInit(void)
  47. {
  48.  SPI_InitTypeDef SPIInit;
  49.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  50.  SPI_StructInit(&SPIInit);
  51.  SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  52.  SPIInit.SPI_Mode = SPI_Mode_Master;
  53.  SPIInit.SPI_CPOL = SPI_CPOL_Low;
  54.  SPIInit.SPI_CPHA = SPI_CPHA_1Edge;
  55.  SPIInit.SPI_NSS = SPI_NSS_Soft;
  56.  SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
  57.  SPI_Init(SPI1, &SPIInit);
  58.  SPI_Cmd(SPI1, ENABLE);
  59. }
  60. uint8_t SPI_SEND(uint8_t byte)
  61. {
  62.  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
  63.  SPI_I2S_SendData(SPI1, byte);
  64.  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
  65.  return SPI_I2S_ReceiveData(SPI1);
  66. }
  67. void MCP_WRITE(uint8_t rejestr, uint8_t wartosc)
  68. {
  69.  GPIO_ResetBits(CS_LINE, CS_PIN);
  70.  SPI_SEND(0x40);
  71.  SPI_SEND(rejestr);
  72.  SPI_SEND(wartosc);
  73.  GPIO_SetBits(CS_LINE, CS_PIN);
  74. }

Dane najłatwiej przesyłać w postaci tablic zapalając i gasząc z odpowiednią częstotliwością. Poniżej dwie tablice dla dwóch diod. Pozwalają one na regulacje co 10%.

  1. uint8_t PWM10_Value_Table2[10] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  2. uint8_t PWM20_Value_Table2[10] = { 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  3. uint8_t PWM30_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  4. uint8_t PWM40_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  5. uint8_t PWM50_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
  6. uint8_t PWM60_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00 };
  7. uint8_t PWM70_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00 };
  8. uint8_t PWM80_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00 };
  9. uint8_t PWM90_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00 };
  10. uint8_t PWM10_Value_Table5[10] = { 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  11. uint8_t PWM20_Value_Table5[10] = { 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  12. uint8_t PWM30_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  13. uint8_t PWM40_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  14. uint8_t PWM50_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  15. uint8_t PWM60_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E };
  16. uint8_t PWM70_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E };
  17. uint8_t PWM80_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E };
  18. uint8_t PWM90_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E };

Można także przygotować tablicę wielowymiarową z zawartymi wszystkimi potrzebnymi danymi. Na tych tablicach można dokonywać operacji poprzez operacje bitowe jak przesunięcia, negacja czy ustawianie.

Dane tablice wywołać można np. w pętli for, przechodząc po każdym z elementów.

74HC595


Drugi rejestr przesuwny jest włączany w podobny sposób, różnica jest jedynie w sposobie podłączenia oraz przesyłania danych:

Podłączenie jest następujące:

  • DS - Data, SPI1 MOSI, PA5
  • OE - PC0
  • ST_CP - PC3
  • SH_CP - SPI Clock; PA5
  • MR - GND
  • Q7 - NC

Dane do PWM wgrywane są na tej samej zasadzie:

  1. uint8_t PWM10_Value_Table2[10] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  2. uint8_t PWM20_Value_Table2[10] = { 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  3. uint8_t PWM30_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  4. uint8_t PWM40_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  5. uint8_t PWM50_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
  6. uint8_t PWM60_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00 };
  7. uint8_t PWM70_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00 };
  8. uint8_t PWM80_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00 };
  9. uint8_t PWM90_Value_Table2[10] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00 };
  10. uint8_t PWM10_Value_Table5[10] = { 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  11. uint8_t PWM20_Value_Table5[10] = { 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  12. uint8_t PWM30_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  13. uint8_t PWM40_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  14. uint8_t PWM50_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E };
  15. uint8_t PWM60_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E, 0x0E };
  16. uint8_t PWM70_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E, 0x0E };
  17. uint8_t PWM80_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E, 0x0E };
  18. uint8_t PWM90_Value_Table5[10] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x0E };
  19. //Deklaracja pinów
  20. #define CS_PIN   GPIO_Pin_0
  21. #define CS_LINE   GPIOC
  22. #define SCK_PIN   GPIO_Pin_5
  23. #define SCK_LINE  GPIOA
  24. #define MISO_PIN  GPIO_Pin_6
  25. #define MISO_LINE  GPIOA
  26. #define MOSI_PIN  GPIO_Pin_7
  27. #define MOSI_LINE  GPIOA
  28. #define ST_CP_PIN   GPIO_Pin_3
  29. #define ST_CP_LINE   GPIOC
  30. void GPIOInit(void)
  31. {
  32.  GPIO_InitTypeDef GPIOInit;
  33.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
  34.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
  35.  GPIO_StructInit(&GPIOInit);
  36.  //piny SCK, MOSI
  37.  GPIOInit.GPIO_Pin = SCK_PIN|MOSI_PIN; // SCK, MOSI
  38.  GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP;
  39.  GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
  40.  GPIO_Init(SCK_LINE, &GPIOInit);
  41.  //Pin Miso
  42.  GPIOInit.GPIO_Pin = MISO_PIN; // MISO
  43.  GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  44.  GPIOInit.GPIO_Speed = GPIO_Speed_10MHz;
  45.  GPIO_Init(MISO_LINE, &GPIOInit);
  46.  //Pin CS
  47.  GPIOInit.GPIO_Pin = CS_PIN;
  48.  GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP;
  49.  GPIOInit.GPIO_Speed = GPIO_Speed_10MHz;
  50.  GPIO_Init(CS_LINE, &GPIOInit);
  51.  GPIO_SetBits(CS_LINE, CS_PIN);
  52.  GPIOInit.GPIO_Pin = ST_CP_PIN;
  53.  GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP;
  54.  GPIOInit.GPIO_Speed = GPIO_Speed_10MHz;
  55.  GPIO_Init(ST_CP_LINE, &GPIOInit);
  56. }
  57. void SPIInit(void)
  58. {
  59.  SPI_InitTypeDef SPIInit;
  60.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  61.  SPI_StructInit(&SPIInit);
  62.  SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  63.  SPIInit.SPI_Mode = SPI_Mode_Master;
  64.  SPIInit.SPI_CPOL = SPI_CPOL_Low;
  65.  SPIInit.SPI_CPHA = SPI_CPHA_1Edge;
  66.  SPIInit.SPI_DataSize = SPI_DataSize_8b;
  67.  SPIInit.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;;
  68.  SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
  69.  SPI_Init(SPI1, &SPIInit);
  70.  SPI_Cmd(SPI1, ENABLE);
  71. }
  72. uint8_t SPI_SEND(uint8_t byte)
  73. {
  74.  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
  75.  SPI_I2S_SendData(SPI1, byte);
  76.  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
  77.  return SPI_I2S_ReceiveData(SPI1);
  78. }
  79. void HC595_WRITE(uint8_t wartosc)
  80. {
  81.     GPIO_ResetBits(ST_CP_LINE, ST_CP_PIN);
  82.     GPIO_SetBits(CS_LINE, CS_PIN);
  83.     SPI_SEND(wartosc);
  84.     GPIO_SetBits(ST_CP_LINE, ST_CP_PIN);
  85.     GPIO_ResetBits(CS_LINE, CS_PIN);
  86. }